package adapter

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/sirupsen/logrus"
	"math"
	"migration-tool/adapter/gitee"
	"migration-tool/common"
	"migration-tool/target"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

const (
	GiteeErrorMsgNotFound404         = "404 Not Found"
	GiteeErrorMsgPullRequestNotFound = "Pull Request not found"
	GiteeErrorMsgprojectNotFound     = "Not Found Project"
	GiteeErrorMsgUnauthorized401     = "401 Unauthorized"
	GiteeErrorMsgGroupError          = "Group"
	GiteeErrorMsgBlock               = "429 Too Many Requests"
	GiteeErrorMsgUserNotFound        = "未找到用户"
)

var GiteeErrorBlock error
var GiteeRoleMap map[string]int64

// 这个是用来统计Id，用来填充API获取Group信息的
var GiteeGroupIdList []int64

// 这个是用来统计label所需group的，这里会比GroupIdList多一个，因为企业本身可能保有仓库
var GiteeGroupPathList []string

func init() {
	GiteeErrorBlock = errors.New("碰见了Gitee限流，任务尚未失败，正在动态调整。。。")

	GiteeRoleMap = make(map[string]int64)

	// TODO：这个需要考虑配合根据配置文件解析，否则可能造成Euler导出之后修改代码，龙蜥导出也需要修改代码
	GiteeRoleMap[GiteeEnterpriseRoleEnterpriseOwner] = AtomGitOwner
	GiteeRoleMap[GiteeEnterpriseRoleAdmin] = AtomGitAdmin
	GiteeRoleMap[GiteeEnterpriseRoleSuperAdmin] = AtomGitAdmin
	GiteeRoleMap[GiteeEnterpriseRoleMember] = AtomGitDeveloper
	GiteeRoleMap[GiteeEnterpriseRoleCustomRole] = AtomGitDeveloper
	// 没有任何Role的角色，定为浏览者
	GiteeRoleMap[GiteeEnterpriseRoleNull] = AtomGitReporter
	GiteeRoleMap[GiteeEnterpriseRoleOutsourcedMember] = AtomGitReporter
	GiteeRoleMap[GiteeEnterpriseRoleHumanResources] = AtomGitReporter

}

const (
	// 企业信息相关
	GiteeV8OpenApiGetAllAuthorizedEnterprise = "https://api.gitee.com/enterprises/list?access_token={{.EnterpriseToken}}&sort=name&direction=desc&per_page={{.PageSize}}&page="
	GiteeV8OpenApiGetEnterpriseDetail        = "https://api.gitee.com/enterprises/{{.EnterpriseId}}?access_token={{.EnterpriseToken}}"
	GiteeV8OpenApiGetEnterpriseMembers       = "https://api.gitee.com/enterprises/{{.EnterpriseId}}/members?access_token={{.EnterpriseToken}}&per_page={{.PageSize}}&page="

	// 按照企业Token获取所有仓库
	GiteeV8OpenApiGetAllAuthorizedRepos = "https://api.gitee.com/enterprises/{{.EnterpriseId}}/projects?access_token={{.EnterpriseToken}}&per_page={{.PageSize}}&page="
	GiteeV8OpenApiGetRepoDetail         = "https://api.gitee.com/enterprises/{{.EnterpriseId}}/projects/{{.RepoId}}?access_token={{.EnterpriseToken}}"

	// Labels是企业共用的，不分库获取
	GiteeV8OpenApiGetEnterpriseLabels = "https://api.gitee.com/enterprises/{{.EnterpriseId}}/labels?access_token={{.EnterpriseToken}}&per_page={{.PageSize}}&page="

	// V8版本的比V5版本获取的信息要详细，尤其是还包括了权限信息
	GiteeV8OpenApiGetRepoMembers = "https://api.gitee.com/enterprises/{{.EnterpriseId}}/projects/{{.RepoId}}/members?access_token={{.EnterpriseToken}}&per_page={{.PageSize}}&page="

	// 获取到的"merger": "422679;5582920;5594572;8932001"里边都是Id/Admin，需要和memberDetail共同使用
	GiteeV8OpenApiGetProtectBranchSetting = "https://api.gitee.com/enterprises/{{.EnterpriseId}}/projects/{{.RepoId}}/branches?access_token={{.EnterpriseToken}}&per_page={{.PageSize}}&page="
	GiteeV8OpenApiGetMemberDetail         = "https://api.gitee.com/enterprises/{{.EnterpriseId}}/members/{{.UserId}}?access_token={{.EnterpriseToken}}"

	// V8的milestone比V5的数据要详细
	GiteeV8OpenApiGetMileStones = "https://api.gitee.com/enterprises/{{.EnterpriseId}}/milestones?access_token={{.EnterpriseToken}}&project_id={{.RepoId}}&per_page={{.PageSize}}&page="

	// 组概念
	GiteeV8OpenApiGetAllGroups    = "https://api.gitee.com/enterprises/{{.EnterpriseId}}/groups?access_token={{.EnterpriseToken}}&page_size={{.PageSize}}&page="
	GiteeV8OpenApiGetGroupDetail  = "https://api.gitee.com/enterprises/{{.EnterpriseId}}/groups/{{.RepoGroupId}}?access_token={{.EnterpriseToken}}"
	GiteeV8OpenApiGetGroupRepos   = "https://api.gitee.com/enterprises/{{.EnterpriseId}}/groups/{{.RepoGroupId}}/projects?access_token={{.EnterpriseToken}}&per_page={{.PageSize}}&page="
	GiteeV8OpenApiGetGroupMembers = "https://api.gitee.com/enterprises/{{.EnterpriseId}}/groups/{{.RepoGroupId}}/members?access_token={{.EnterpriseToken}}&per_page={{.PageSize}}&page="

	// ========以下均是V5版本的API==============
	GiteeV5OpenApiGetAllAuthorizedRepos            = "https://gitee.com/api/v5/user/repos?access_token={{.Token}}&sort=full_name&type=all&per_page={{.PageSize}}&page="
	GiteeV5OpenApiGetOrganization                  = "https://gitee.com/api/v5/user/orgs?access_token={{.Token}}&per_page={{.PageSize}}&page="
	GiteeV5OpenApiGetUserEmail                     = "https://gitee.com/api/v5/users/{{.UserName}}?access_token={{.Token}}"
	GiteeV5OpenApiGetAllUsersForOneOrganization    = "https://gitee.com/api/v5/orgs/{{.RepoGroupName}}/members?access_token={{.Token}}&role=all&per_page={{.PageSize}}&page="
	GiteeV5OpenApiGetAllAuthorizedReposForOneGroup = "https://gitee.com/api/v5/orgs/{{.RepoGroupName}}/repos?access_token={{.Token}}&type=all&per_page={{.PageSize}}&page="

	// 一个仓库需要获取的信息
	GiteeV5OpenApiGetRepoMembers                = "https://gitee.com/api/v5/repos/{{.RepoGroupName}}/{{.RepoName}}/collaborators?access_token={{.Token}}&per_page={{.PageSize}}&page="
	GiteeV5OpenApiGetRepoContributorsCommitters = "https://gitee.com/api/v5/repos/{{.RepoGroupName}}/{{.RepoName}}/contributors?access_token={{.Token}}&type=committers"
	GiteeV5OpenApiGetRepoContributorsAuthors    = "https://gitee.com/api/v5/repos/{{.RepoGroupName}}/{{.RepoName}}/contributors?access_token={{.Token}}&type=authors"

	// tester和assigner，代码评审规则是在仓库信息中的
	GiteeV5OpenApiGetRepoMetaInfo                  = "https://gitee.com/api/v5/repos/{{.RepoGroupName}}/{{.RepoName}}?access_token={{.Token}}"
	GiteeV5OpenApiGetAllReleases                   = "https://gitee.com/api/v5/repos/{{.RepoGroupName}}/{{.RepoName}}/releases?access_token={{.Token}}&direction=desc&per_page={{.PageSize}}&page="
	GiteeV5OpenApiGetPushRuleSetting               = "https://gitee.com/api/v5/repos/{{.RepoGroupName}}/{{.RepoName}}/push_config?access_token={{.Token}}"
	GiteeV5OpenApiGetWebHookSetting                = "https://gitee.com/api/v5/repos/{{.RepoGroupName}}/{{.RepoName}}/hooks?access_token={{.Token}}&per_page={{.PageSize}}&page="
	GiteeV5OpenApiGetDeployedKeysSetting           = "https://gitee.com/api/v5/repos/{{.RepoGroupName}}/{{.RepoName}}/keys?access_token={{.Token}}&per_page={{.PageSize}}&page="
	GiteeV5OpenApiGetCanBeDeployedKeysSetting      = "https://gitee.com/api/v5/repos/{{.RepoGroupName}}/{{.RepoName}}/keys/available?access_token={{.Token}}&per_page={{.PageSize}}&page="
	GiteeV5OpenApiGetAllMergeRequests              = "https://gitee.com/api/v5/repos/{{.RepoGroupName}}/{{.RepoName}}/pulls/?access_token={{.Token}}&state=all&sort=created&direction=asc&per_page={{.PageSize}}&page="
	GiteeV5OpenApiGetIncrementMergeRequests        = "https://gitee.com/api/v5/repos/{{.RepoGroupName}}/{{.RepoName}}/pulls/?access_token={{.Token}}&state=all&sort=updated&direction=desc&per_page={{.PageSize}}&page="
	GiteeV5OpenApiGetAllCommentsForOneMergeRequest = "https://gitee.com/api/v5/repos/{{.RepoGroupName}}/{{.RepoName}}/pulls/{{.DataId}}/comments?access_token={{.Token}}&sort=created&direction=asc&per_page={{.PageSize}}&page="
	GiteeV5OpenApiGetAllIssues                     = "https://gitee.com/api/v5/repos/{{.RepoGroupName}}/{{.RepoName}}/issues?access_token={{.Token}}&state=all&sort=created&direction=asc&per_page={{.PageSize}}&page="
	GiteeV5OpenApiGetIncrementIssues               = "https://gitee.com/api/v5/repos/{{.RepoGroupName}}/{{.RepoName}}/issues?access_token={{.Token}}&state=all&sort=updated&direction=desc&per_page={{.PageSize}}&page="
	GiteeV5OpenApiGetAllCommentsForOneIssue        = "https://gitee.com/api/v5/repos/{{.RepoGroupName}}/{{.RepoName}}/issues/{{.DataId}}/comments?access_token={{.Token}}&sort=created&order=asc&per_page={{.PageSize}}&page="

	GiteeV5OpenApiGetAllCommentsForAllCommit = "https://gitee.com/api/v5/repos/{{.RepoGroupName}}/{{.RepoName}}/comments?access_token={{.Token}}&order=desc&per_page={{.PageSize}}&page="
	GiteeV5OpenApiGetAllForkedRepos          = "https://gitee.com/api/v5/repos/{{.RepoGroupName}}/{{.RepoName}}/forks?access_token={{.Token}}&sort=newest&per_page={{.PageSize}}&page="
	GiteeV5OpenApiGetAllStargazedRepoUsers   = "https://gitee.com/api/v5/repos/{{.RepoGroupName}}/{{.RepoName}}/stargazers?access_token={{.Token}}&per_page={{.PageSize}}&page="

	// 分支保护规则的API有bug，使用cookie获取的URL如下：
	GiteeV5OpenApiGetProtectBranchSetting = "https://gitee.com/{{.RepoGroupName}}/{{.RepoName}}/protection_rules?page_size={{.PageSize}}&page="
	// 有bug的API
	GiteeV5OpenApiGetBranchProtectionSetting = "https://gitee.com/api/v5/repos/{{.RepoGroupName}}/{{.RepoName}}/branches/{{.BranchName}}?access_token={{.Token}}&page_size={{.PageSize}}&page="

	GiteeHttpBaseCloneUrl = "https://gitee.com/"
	GiteeSshBaseCloneUrl  = "git@gitee.com:"

	GiteeEnterpriseRoleMember           = "member"
	GiteeEnterpriseRoleOutsourcedMember = "outsourced_member"
	GiteeEnterpriseRoleHumanResources   = "human_resources"
	GiteeEnterpriseRoleAdmin            = "admin"
	GiteeEnterpriseRoleSuperAdmin       = "super_admin"
	GiteeEnterpriseRoleEnterpriseOwner  = "enterprise_owner"
	GiteeEnterpriseRoleCustomRole       = "custom_role"
	GiteeEnterpriseRoleNull             = ""

	AtomGitOwner                   = 100
	AtomGitAdmin                   = 40
	AtomGitDeveloper               = 30
	AtomGitReporter                = 20
	AtomGitMergeRequestModeGeneral = "general"

	GiteeProtectionRoleAdmin     = "admin"
	GiteeProtectionRoleDeveloper = "developer"
	GiteeProtectionRoleNone      = "none"
	GiteeBranchTypeNormal        = 0
	GiteeBranchTypeProtected     = 1
	GiteeBranchTypeReadOnly      = 2

	GiteeEventIssue                    = "issues"
	GiteeEventIssueComment             = "issue_comment"
	GiteeEventLabel                    = "label"
	GiteeEventMileStone                = "milestone"
	GiteeEventPullRequest              = "pull_requests"
	GiteeEventPullRequestReviewComment = "pull_request_review_comment"
	GiteeEventPush                     = "push"

	AtomGitRequestTypeJson               = 1
	AtomGitRequestTypeXwwwformUrlencoded = 2
)

type giteeRequestArgs struct {
	RequestArgs
	TaskName    string
	NoRecordLog bool           // 默认为false，即记录日志，在特定的场景置为true，不记录日志
	Log         *logrus.Logger // 为nil时，不记录日志
}

type giteeUserDetailArgs struct {
	Token           string
	EnterpriseToken string
	EnterpriseId    int64
	User            *target.AtomGitUser
}

func giteeDoGetRequest(requestArgs giteeRequestArgs) error {
	recordArgs := RecordArgs{
		Log:      requestArgs.Log,
		TaskName: requestArgs.TaskName,
		Export:   true,
	}
	recordUrl := requestArgs.RealUrl
	if strings.LastIndex(recordUrl, "?") != -1 {
		recordUrl = recordUrl[:strings.LastIndex(recordUrl, "?")]
	}
	for {
		err := DoGetRequest(requestArgs.RequestArgs)
		if err == nil {
			return nil
		} else {
			if err == GiteeErrorBlock {
				if recordArgs.Log != nil && requestArgs.NoRecordLog == false {
					recordArgs.Msg = "限流了一次，请求URL为：" + recordUrl
					recordTask(recordArgs)
				}
				// 这里可以增加全局的，针对Gitee限流出现的策略
				continue
			}
			if recordArgs.Log != nil && requestArgs.NoRecordLog == false {
				recordArgs.Msg = "请求出现错误，请求URL为：" + recordUrl
				recordArgs.Err = err
				recordTask(recordArgs)
			}
			return err
		}
	}
}

func giteeV8GetUserDetail(args giteeUserDetailArgs) error {
	if args.User.Email == "" {
		tmplArgs := TmplArgs{
			UserId:          args.User.Id,
			EnterpriseToken: args.EnterpriseToken,
			EnterpriseId:    args.EnterpriseId,
			StringTmpl:      GiteeV8OpenApiGetMemberDetail,
		}
		realUrl, err := FillParamIntoStringTmpl(tmplArgs)
		if err != nil {
			return err
		}

		var data gitee.V8EnterpriseMember
		requestArgs := giteeRequestArgs{
			RequestArgs: RequestArgs{
				ExportPlatformName: GiteePlatformName,
				RealUrl:            realUrl,
				RetVal:             &data,
			},
			TaskName:    "",
			NoRecordLog: true,
			Log:         nil,
		}
		if err = giteeDoGetRequest(requestArgs); err != nil {
			return err
		}

		args.User.UserName = data.Username
		args.User.Name = data.Name
		args.User.Email = data.Email
		args.User.Phone = data.Phone
	}
	return nil
}

func giteeGetUserDetail(args giteeUserDetailArgs) error {
	if args.User.Email == "" {
		tmplArgs := TmplArgs{
			Token:      args.Token,
			UserName:   args.User.UserName,
			StringTmpl: GiteeV5OpenApiGetUserEmail,
		}
		realUrl, err := FillParamIntoStringTmpl(tmplArgs)
		if err != nil {
			return err
		}
		var data gitee.UserInfo
		requestArgs := giteeRequestArgs{
			RequestArgs: RequestArgs{
				ExportPlatformName: GiteePlatformName,
				RealUrl:            realUrl,
				RetVal:             &data,
			},
			TaskName:    "",
			NoRecordLog: true,
			Log:         nil,
		}
		if err = giteeDoGetRequest(requestArgs); err != nil {
			return err
		}
		args.User.Email = data.Email
	}

	return nil
}

// ==========以下开始都是业务函数==============

func giteeGetAllRepoInfo(cfg *common.Config) (RepoInfoArray, error) {
	if cfg.BaseConfig.Export {
		if cfg.BaseConfig.EnterpriseToken != "" {
			return giteeGetAllRepoInfoForExport(cfg)
		} else {
			return giteeGetAllRepoInfoForExportNoEnterpriseToken(cfg)
		}
	} else {
		return giteeGetAllRepoInfoForImport(cfg)
	}
}

func giteeGetAllRepoInfoForImport(cfg *common.Config) (RepoInfoArray, error) {
	tmplArgs := TmplArgs{
		ExportDataDir:      cfg.BaseConfig.ExportDataDir,
		ExportPlatformName: GiteePlatformName,
		FileName:           JsonFileNameAllRepoInfo,
		EnterpriseName:     cfg.BaseConfig.EnterpriseName,
		StringTmpl:         ExportEnterpriseFileTmpl,
	}

	filePath, err := FillParamIntoStringTmpl(tmplArgs)

	var repoInfo RepoInfoArray
	_, err = os.Stat(filePath)
	if err == nil {
		data, err := os.ReadFile(filePath)
		if err != nil {
			return nil, err
		}
		err = json.Unmarshal(data, &repoInfo)
		if err != nil {
			return nil, err
		}
	} else if os.IsNotExist(err) {
		panic("请先进行一次导出工作，以便生成gitee-metaData/repo-info.json文件供导入使用")
	}

	return repoInfo, nil
}

func giteeGetAllRepoInfoForExport(cfg *common.Config) (RepoInfoArray, error) {
	dataArgs := DataArgs{
		ExportDataDir:      cfg.BaseConfig.ExportDataDir,
		FileName:           JsonFileNameAllRepoInfo,
		ExportPlatformName: GiteePlatformName,
		EnterpriseName:     cfg.BaseConfig.EnterpriseName,
		FilePathTmpl:       ExportEnterpriseFileTmpl,
	}

	// filePath为之后保存仓库信息文件的文件路径
	filePath, err := MkdirFromStringTmpl(dataArgs)
	if cfg.BaseConfig.UseProgress {
		// 代表使用Cache加速，但是Cache加速只在一个时间端有效
		stat, err := os.Stat(filePath)
		if err == nil {
			interval := time.Now().Sub(stat.ModTime())
			// 在时间间隔内，就进行读缓存加速
			if interval.Seconds() < RepoInfoCacheIntervalSeconds {
				var repoInfo RepoInfoArray
				data, err := os.ReadFile(filePath)
				if err != nil {
					return nil, err
				}
				err = json.Unmarshal(data, &repoInfo)
				if err != nil {
					return nil, err
				}

				groupIdMap := make(map[int64]struct{})
				groupPathMap := make(map[string]struct{})
				for _, val := range repoInfo {
					groupPathMap[val.RepoGroupName] = struct{}{}
					if val.RepoGroupId != val.EnterpriseId {
						groupIdMap[val.RepoGroupId] = struct{}{}
					}
				}

				for key := range groupIdMap {
					GiteeGroupIdList = append(GiteeGroupIdList, key)
				}
				for key := range groupPathMap {
					GiteeGroupPathList = append(GiteeGroupPathList, key)
				}
				// 返回所有获取的仓库
				return repoInfo, nil
			}
		}
	}

	var enterpriseId int64
	enterpriseId = -1
	page := 1
	tmplArgs := TmplArgs{
		PageSize:        cfg.BaseConfig.PageSize,
		EnterpriseToken: cfg.BaseConfig.EnterpriseToken,
		StringTmpl:      GiteeV8OpenApiGetAllAuthorizedEnterprise,
	}
	baseUrl, err := FillParamIntoStringTmpl(tmplArgs)
	if err != nil {
		return nil, err
	}

	for {
		realUrl := baseUrl + strconv.Itoa(page)
		page += 1
		var enterpriseList gitee.V8EnterpriseList
		requestArgs := giteeRequestArgs{
			RequestArgs: RequestArgs{
				ExportPlatformName: GiteePlatformName,
				RealUrl:            realUrl,
				RetVal:             &enterpriseList,
			},
			TaskName: "get-enterprise-list",
			Log:      cfg.GlobalLog,
		}

		if err = giteeDoGetRequest(requestArgs); err != nil {
			return nil, err
		}

		if len(enterpriseList.Data) == 0 {
			break
		}

		for _, val := range enterpriseList.Data {
			if val.Name == cfg.BaseConfig.EnterpriseName {
				enterpriseId = val.ID
				break
			}
		}

		if enterpriseId != -1 {
			break
		}
	}

	if enterpriseId == -1 {
		return nil, errors.New("未找到企业信息，请修改配置文件")
	}

	workerNum := 0
	var requestError error
	requestError = nil
	repoMap := sync.Map{}
	totalSize := atomic.Int64{}
	totalSize.Store(0)
	groupIdMap := sync.Map{}
	groupPathMap := sync.Map{}
	onceQuery := func(queryPage int, baseQueryUrl string) error {
		realUrl := baseQueryUrl + strconv.Itoa(queryPage)

		var data gitee.V8RepositoryList
		requestArgs := giteeRequestArgs{
			RequestArgs: RequestArgs{
				ExportPlatformName: GiteePlatformName,
				RealUrl:            realUrl,
				RetVal:             &data,
			},
			TaskName: "get-repository-list",
			Log:      cfg.GlobalLog, //测试时可以记录一下，看一下限流的情况
		}
		if err := giteeDoGetRequest(requestArgs); err != nil {
			return err
		}

		if len(data.Data) == 0 {
			return nil
		}

		for _, val := range data.Data {
			var oneRepo RepoInfo
			oneRepo.RepoId = val.ID
			oneRepo.RepoGroupId = val.Namespace.ID
			oneRepo.RepoGroupName = val.Namespace.Path
			oneRepo.RepoName = val.Path
			oneRepo.EnterpriseId = enterpriseId
			oneRepo.CodeCloneHttpUrl = strings.Join([]string{GiteeHttpBaseCloneUrl, val.PathWithNamespace, ".git"}, "")
			// 这里假设wiki都是存在的
			oneRepo.WikiEnable = true
			if val.RepoSize == 0 {
				totalSize.Add(1)
			} else {
				totalSize.Add(val.RepoSize)
			}
			repoMap.Store(val.ID, &oneRepo)
			groupIdMap.Store(val.Namespace.ID, val.Namespace.ID)
			groupPathMap.Store(val.Namespace.Path, val.Namespace.Path)
		}
		return nil
	}

	startQuery := func(baseQueryUrl string, wg *sync.WaitGroup) {
		for idx := 0; idx < workerNum; idx++ {
			queryPage := page
			page += 1
			wg.Add(1)
			go func(queryPage int) {
				//cfg.GlobalLog.Infof("执行一次 页号为：%d", queryPage)
				err := onceQuery(queryPage, baseQueryUrl)
				if err != nil {
					requestError = err
				}
				wg.Done()
			}(queryPage)
		}
	}

	tmplArgs = TmplArgs{
		PageSize:        cfg.BaseConfig.PageSize,
		EnterpriseToken: cfg.BaseConfig.EnterpriseToken,
		EnterpriseId:    enterpriseId,
		StringTmpl:      GiteeV8OpenApiGetAllAuthorizedRepos,
	}
	baseUrl, err = FillParamIntoStringTmpl(tmplArgs)
	if err != nil {
		return nil, err
	}
	realUrl := baseUrl + strconv.Itoa(1)

	var getTotalCount gitee.V8RepositoryList
	requestArgs := giteeRequestArgs{
		RequestArgs: RequestArgs{
			ExportPlatformName: GiteePlatformName,
			RealUrl:            realUrl,
			RetVal:             &getTotalCount,
		},
		TaskName: "get-repository-list",
		Log:      cfg.GlobalLog, //测试时可以记录一下，看一下限流的情况
	}
	if err := giteeDoGetRequest(requestArgs); err != nil {
		return nil, err
	}
	workerNum = int(getTotalCount.TotalCount)/cfg.BaseConfig.PageSize + 1

	// 根据企业Token请求仓库
	page = 1
	waitGroup := &sync.WaitGroup{}
	startQuery(baseUrl, waitGroup)
	waitGroup.Wait()

	if requestError != nil {
		return nil, requestError
	}

	var authorizedRepos RepoInfoArray
	var rangeError error
	rangeError = nil
	repoMap.Range(func(key, val any) bool {
		v, ok := val.(*RepoInfo)
		if !ok {
			rangeError = errors.New("userMap中数据出现错误")
			return false
		}
		authorizedRepos = append(authorizedRepos, *v)
		return true
	})

	groupIdMap.Range(func(key, val any) bool {
		v, ok := val.(int64)
		if !ok {
			rangeError = errors.New("groupIdMap中数据出现错误")
			return false
		}
		if v != enterpriseId {
			GiteeGroupIdList = append(GiteeGroupIdList, v)
		}
		return true
	})

	groupPathMap.Range(func(key, val any) bool {
		v, ok := val.(string)
		if !ok {
			rangeError = errors.New("groupPathMap中数据出现错误")
			return false
		}
		GiteeGroupPathList = append(GiteeGroupPathList, v)
		return true
	})

	if rangeError != nil {
		return nil, rangeError
	}

	data, err := json.Marshal(authorizedRepos)
	if err != nil {
		return nil, err
	}
	err = os.WriteFile(filePath, data, os.ModePerm)
	if err != nil {
		return nil, err
	}

	dataArgs = DataArgs{
		ExportDataDir:      cfg.BaseConfig.ExportDataDir,
		FileName:           FileNameTotalRepoSize,
		ExportPlatformName: GiteePlatformName,
		EnterpriseName:     cfg.BaseConfig.EnterpriseName,
		FilePathTmpl:       ExportEnterpriseFileTmpl,
	}

	// filePath为之后保存仓库信息文件的文件路径
	filePath, err = MkdirFromStringTmpl(dataArgs)

	var size string
	if totalSize.Load() > 1024 {
		size = strings.Join([]string{
			"仓库总大小约为:",
			strconv.FormatInt(int64(math.Ceil(float64(totalSize.Load())/1024.0)), 10),
			"GB",
		}, "")
	} else {
		size = strings.Join([]string{
			"仓库总大小约为:",
			strconv.FormatInt(totalSize.Load(), 10),
			"MB",
		}, "")
	}

	err = os.WriteFile(filePath, []byte(size), os.ModePerm)
	if err != nil {
		return nil, err
	}

	// 返回所有获取的仓库
	return authorizedRepos, nil
}

// 此函数只做没有企业token时的测试使用，正式迁移时必须拥有企业token
func giteeGetAllRepoInfoForExportNoEnterpriseToken(cfg *common.Config) (RepoInfoArray, error) {
	dataArgs := DataArgs{
		ExportDataDir:      cfg.BaseConfig.ExportDataDir,
		FileName:           JsonFileNameAllRepoInfo,
		ExportPlatformName: GiteePlatformName,
		EnterpriseName:     cfg.BaseConfig.EnterpriseName,
		FilePathTmpl:       ExportEnterpriseFileTmpl,
	}

	// filePath为之后保存仓库信息文件的文件路径
	filePath, err := MkdirFromStringTmpl(dataArgs)
	if cfg.BaseConfig.UseProgress {
		// 代表使用Cache加速，但是Cache加速只在一个时间端有效
		stat, err := os.Stat(filePath)
		if err == nil {
			interval := time.Now().Sub(stat.ModTime())
			// 在时间间隔内，就进行读缓存加速
			if interval.Seconds() < RepoInfoCacheIntervalSeconds {
				var repoInfo RepoInfoArray
				data, err := os.ReadFile(filePath)
				if err != nil {
					return nil, err
				}
				err = json.Unmarshal(data, &repoInfo)
				if err != nil {
					return nil, err
				}
				// 返回所有获取的仓库
				return repoInfo, nil
			}
		}
	}

	var enterpriseId int64
	enterpriseId = -1
	page := 1
	tmplArgs := TmplArgs{
		PageSize:      cfg.BaseConfig.PageSize,
		Token:         cfg.BaseConfig.Token,
		RepoGroupName: cfg.BaseConfig.EnterpriseName,
		StringTmpl:    GiteeV5OpenApiGetAllAuthorizedReposForOneGroup,
	}
	baseUrl, err := FillParamIntoStringTmpl(tmplArgs)
	if err != nil {
		return nil, err
	}

	for {
		realUrl := baseUrl + strconv.Itoa(page)
		page += 1
		var repoInfo []gitee.Repository
		requestArgs := giteeRequestArgs{
			RequestArgs: RequestArgs{
				ExportPlatformName: GiteePlatformName,
				RealUrl:            realUrl,
				RetVal:             &repoInfo,
			},
			TaskName: "get-enterprise-id-no-enterpriseToken",
			Log:      cfg.GlobalLog,
		}

		if err = giteeDoGetRequest(requestArgs); err != nil {
			return nil, err
		}

		if len(repoInfo) == 0 {
			break
		}

		for _, val := range repoInfo {
			enterpriseId = val.Enterprise.ID
		}

		if enterpriseId != -1 {
			break
		}
	}

	if enterpriseId == -1 {
		return nil, errors.New("未找到企业信息，请修改配置文件")
	}

	workerNum := 0
	var requestError error
	requestError = nil
	repoMap := sync.Map{}
	totalSize := atomic.Int64{}
	totalSize.Store(0)

	onceQuery := func(queryPage int, baseQueryUrl string) error {
		realUrl := baseQueryUrl + strconv.Itoa(queryPage)

		var data []gitee.Repository
		requestArgs := giteeRequestArgs{
			RequestArgs: RequestArgs{
				ExportPlatformName: GiteePlatformName,
				RealUrl:            realUrl,
				RetVal:             &data,
			},
			TaskName: "get-repository-list",
			Log:      cfg.GlobalLog, //测试时可以记录一下，看一下限流的情况
		}
		if err := giteeDoGetRequest(requestArgs); err != nil {
			return err
		}

		if len(data) == 0 {
			return nil
		}

		for _, val := range data {
			var oneRepo RepoInfo
			oneRepo.RepoId = val.ID
			oneRepo.RepoGroupId = val.Namespace.ID
			oneRepo.RepoGroupName = val.Namespace.Path
			oneRepo.RepoName = val.Path
			oneRepo.EnterpriseId = enterpriseId
			oneRepo.CodeCloneHttpUrl = val.HTMLURL
			oneRepo.WikiEnable = val.HasWiki
			repoMap.Store(val.ID, &oneRepo)
		}
		return nil
	}

	startQuery := func(baseQueryUrl string, wg *sync.WaitGroup) {
		for idx := 0; idx < workerNum; idx++ {
			queryPage := page
			page += 1
			wg.Add(1)
			go func(queryPage int) {
				//cfg.GlobalLog.Infof("执行一次 页号为：%d", queryPage)
				err := onceQuery(queryPage, baseQueryUrl)
				if err != nil {
					requestError = err
				}
				wg.Done()
			}(queryPage)
		}
	}

	workerNum = 2
	// 根据企业Token请求仓库
	page = 1
	waitGroup := &sync.WaitGroup{}
	startQuery(baseUrl, waitGroup)
	waitGroup.Wait()

	if requestError != nil {
		return nil, requestError
	}

	var authorizedRepos RepoInfoArray
	var rangeError error
	rangeError = nil
	repoMap.Range(func(key, val any) bool {
		v, ok := val.(*RepoInfo)
		if !ok {
			rangeError = errors.New("userMap中数据出现错误")
			return false
		}
		authorizedRepos = append(authorizedRepos, *v)
		return true
	})

	if rangeError != nil {
		return nil, rangeError
	}

	data, err := json.Marshal(authorizedRepos)
	if err != nil {
		return nil, err
	}
	err = os.WriteFile(filePath, data, os.ModePerm)
	if err != nil {
		return nil, err
	}

	dataArgs = DataArgs{
		ExportDataDir:      cfg.BaseConfig.ExportDataDir,
		FileName:           FileNameTotalRepoSize,
		ExportPlatformName: GiteePlatformName,
		EnterpriseName:     cfg.BaseConfig.EnterpriseName,
		FilePathTmpl:       ExportEnterpriseFileTmpl,
	}

	// filePath为之后保存仓库信息文件的文件路径
	filePath, err = MkdirFromStringTmpl(dataArgs)

	var size string
	if totalSize.Load() > 1024 {
		size = strings.Join([]string{
			"仓库总大小约为:",
			strconv.FormatInt(int64(math.Ceil(float64(totalSize.Load())/1024.0)), 10),
			"GB",
		}, "")
	} else {
		size = strings.Join([]string{
			"仓库总大小约为:",
			strconv.FormatInt(totalSize.Load(), 10),
			"MB",
		}, "")
	}

	err = os.WriteFile(filePath, []byte(size), os.ModePerm)
	if err != nil {
		return nil, err
	}

	// 返回所有获取的仓库
	return authorizedRepos, nil
}

// ==========使用v8的不分库全局相关函数==============

// 获取企业信息
func globalGiteeDealEnterprise(args TaskArgs) error {
	recordArgs := RecordArgs{
		Log:      args.Log,
		TaskName: args.TaskName,
		Err:      nil,
		Export:   true,
	}

	tmplArgs := TmplArgs{
		EnterpriseId:    args.RepoInfo.EnterpriseId,
		EnterpriseToken: args.EnterpriseToken,
		StringTmpl:      GiteeV8OpenApiGetEnterpriseDetail,
	}
	realUrl, err := FillParamIntoStringTmpl(tmplArgs)
	if err != nil {
		return err
	}

	var enterprise target.AtomGitEnterprise
	requestArgs := giteeRequestArgs{
		RequestArgs: RequestArgs{
			ExportPlatformName: GiteePlatformName,
			RealUrl:            realUrl,
			RetVal:             &enterprise,
		},
		TaskName: args.TaskName,
		Log:      args.Log,
	}
	if err = giteeDoGetRequest(requestArgs); err != nil {
		return err
	}

	userDetailArgs := giteeUserDetailArgs{
		Token:           args.Token,
		EnterpriseToken: args.EnterpriseToken,
		EnterpriseId:    args.RepoInfo.EnterpriseId,
	}
	userDetailArgs.User = enterprise.Owner
	err = giteeV8GetUserDetail(userDetailArgs)
	if err != nil {
		_ = giteeGetUserDetail(userDetailArgs)
	}

	writeDataArgs := DataArgs{
		ExportDataDir:      args.ExportDataDir,
		FileName:           BinFileNameEnterprise,
		ExportPlatformName: GiteePlatformName,
		EnterpriseName:     args.EnterpriseName,
		FilePathTmpl:       ExportEnterpriseFileTmpl,
		Data:               &enterprise,
		WriteJson:          true,
	}

	_, err = WriteProtoData(writeDataArgs)
	if err != nil {
		return err
	}
	recordArgs.Msg = "落盘企业元信息数据"
	recordTask(recordArgs)

	return nil
}

// 获取企业成员和权限
func globalGiteeDealEnterpriseMember(args TaskArgs) error {
	recordArgs := RecordArgs{
		Log:      args.Log,
		TaskName: args.TaskName,
		Err:      nil,
		Export:   true,
	}

	tmplArgs := TmplArgs{
		EnterpriseId:    args.RepoInfo.EnterpriseId,
		EnterpriseToken: args.EnterpriseToken,
		StringTmpl:      GiteeV8OpenApiGetEnterpriseDetail,
	}
	realUrl, err := FillParamIntoStringTmpl(tmplArgs)
	if err != nil {
		return err
	}

	var enterprise target.AtomGitEnterprise
	requestArgs := giteeRequestArgs{
		RequestArgs: RequestArgs{
			ExportPlatformName: GiteePlatformName,
			RealUrl:            realUrl,
			RetVal:             &enterprise,
		},
		TaskName: args.TaskName,
		Log:      args.Log,
	}
	if err = giteeDoGetRequest(requestArgs); err != nil {
		return err
	}

	tmplArgs = TmplArgs{
		PageSize:        args.PageSize,
		EnterpriseId:    args.RepoInfo.EnterpriseId,
		EnterpriseToken: args.EnterpriseToken,
		StringTmpl:      GiteeV8OpenApiGetEnterpriseMembers,
	}
	baseUrl, err := FillParamIntoStringTmpl(tmplArgs)
	if err != nil {
		return err
	}

	var memberList target.AtomGitEnterpriseMemberList
	memberList.EnterpriseName = args.EnterpriseName
	page := 1

	for {
		realUrl := baseUrl + strconv.Itoa(page)
		page += 1
		var data gitee.V8EnterpriseMemberList
		requestArgs := giteeRequestArgs{
			RequestArgs: RequestArgs{
				ExportPlatformName: GiteePlatformName,
				RealUrl:            realUrl,
				RetVal:             &data,
			},
			TaskName: args.TaskName,
			Log:      args.Log,
		}
		if err = giteeDoGetRequest(requestArgs); err != nil {
			return err
		}

		if len(data.Data) == 0 {
			break
		}

		for _, val := range data.Data {
			var member target.AtomGitMember
			member.User = &target.AtomGitUser{
				Id:       val.ID,
				Name:     val.Name,
				UserName: val.Username,
				Email:    val.Email,
				Phone:    val.Phone,
			}
			//member.AccessLevel = GiteeRoleMap[val.EnterpriseRole.Ident]
			member.AccessLevel = GiteeRoleMap[val.EnterpriseRole.Ident]
			memberList.MemberList = append(memberList.MemberList, &member)
		}

		//recordArgs.Msg = "共获取到" + strconv.Itoa(len(memberList.MemberList)) + "条数据，但还未落盘"
		//recordTask(recordArgs)

		if int64((page-1)*args.PageSize) >= data.TotalCount {
			break
		}
	}

	if len(memberList.MemberList) > 0 {
		writeDataArgs := DataArgs{
			ExportDataDir:      args.ExportDataDir,
			FileName:           BinFileNameEnterpriseMembersProto,
			ExportPlatformName: GiteePlatformName,
			EnterpriseName:     args.EnterpriseName,
			FilePathTmpl:       ExportEnterpriseFileTmpl,
			Data:               &memberList,
		}

		_, err = WriteProtoData(writeDataArgs)
		if err != nil {
			return err
		}

		writeDataArgs = DataArgs{
			ExportDataDir:      args.ExportDataDir,
			FileName:           BinFileNameEnterpriseMembersJson,
			ExportPlatformName: GiteePlatformName,
			EnterpriseName:     args.EnterpriseName,
			FilePathTmpl:       ExportEnterpriseFileTmpl,
			Data:               &memberList,
			WriteJson:          true,
		}

		_, err = WriteProtoData(writeDataArgs)
		if err != nil {
			return err
		}

		recordArgs.Msg = "共落盘" + strconv.Itoa(len(memberList.MemberList)) + "条企业成员信息数据"
	} else {
		recordArgs.Msg = LogMsgNoData
	}
	recordTask(recordArgs)

	return nil
}

// 获取所有Group
func globalGiteeDealGroup(args TaskArgs) error {
	recordArgs := RecordArgs{
		Log:      args.Log,
		TaskName: args.TaskName,
		Err:      nil,
		Export:   true,
	}
	// 获取Group元信息
	tmplArgs := TmplArgs{
		PageSize:        args.PageSize,
		EnterpriseId:    args.RepoInfo.EnterpriseId,
		EnterpriseToken: args.EnterpriseToken,
		StringTmpl:      GiteeV8OpenApiGetAllGroups,
	}
	baseUrl, err := FillParamIntoStringTmpl(tmplArgs)
	if err != nil {
		return err
	}

	var groupList target.AtomGitGroupList
	page := 1
	for {
		realUrl := baseUrl + strconv.Itoa(page)
		page += 1
		var data gitee.V8GroupList
		requestArgs := giteeRequestArgs{
			RequestArgs: RequestArgs{
				ExportPlatformName: GiteePlatformName,
				RealUrl:            realUrl,
				RetVal:             &data,
			},
			TaskName: args.TaskName,
			Log:      args.Log,
		}
		if err = giteeDoGetRequest(requestArgs); err != nil {
			return err
		}

		if len(data.Data) == 0 {
			break
		}

		for _, val := range data.Data {
			var group target.AtomGitGroup

			group.Id = val.ID
			group.Path = val.Path
			group.Name = val.Name
			group.Description = val.Description
			group.Public = val.Public
			group.Creator = &target.AtomGitUser{
				Id:       val.Creator.ID,
				Name:     val.Creator.Name,
				UserName: val.Creator.Username,
			}

			groupList.GroupList = append(groupList.GroupList, &group)
		}

		//recordArgs.Msg = "共获取到" + strconv.Itoa(len(memberList.MemberList)) + "条数据，但还未落盘"
		//recordTask(recordArgs)

		if int64((page-1)*args.PageSize) >= data.TotalCount {
			break
		}
	}

	if len(groupList.GroupList) == 0 && len(GiteeGroupIdList) > 0 {
		for _, groupId := range GiteeGroupIdList {
			tmplArgs = TmplArgs{
				EnterpriseId:    args.RepoInfo.EnterpriseId,
				EnterpriseToken: args.EnterpriseToken,
				RepoGroupId:     groupId,
				StringTmpl:      GiteeV8OpenApiGetGroupDetail,
			}
			realUrl, err := FillParamIntoStringTmpl(tmplArgs)
			if err != nil {
				return err
			}
			var data gitee.V8GroupDetail
			requestArgs := giteeRequestArgs{
				RequestArgs: RequestArgs{
					ExportPlatformName: GiteePlatformName,
					RealUrl:            realUrl,
					RetVal:             &data,
				},
				TaskName: args.TaskName,
				Log:      args.Log,
			}
			if err = giteeDoGetRequest(requestArgs); err != nil {
				return err
			}

			var group target.AtomGitGroup

			group.Id = data.Id
			group.Path = data.Path
			group.Name = data.Name
			group.Description = data.Description
			group.Public = data.Public
			group.Creator = &target.AtomGitUser{
				Id:       data.Creator.ID,
				Name:     data.Creator.Name,
				UserName: data.Creator.Username,
			}
			userDetailArgs := giteeUserDetailArgs{
				Token:           args.Token,
				EnterpriseToken: args.EnterpriseToken,
				EnterpriseId:    args.RepoInfo.EnterpriseId,
			}
			userDetailArgs.User = group.Creator
			err = giteeV8GetUserDetail(userDetailArgs)
			if err != nil {
				_ = giteeGetUserDetail(userDetailArgs)
			}
			groupList.GroupList = append(groupList.GroupList, &group)
		}

	}

	// Group元信息落盘
	if len(groupList.GroupList) > 0 {
		writeDataArgs := DataArgs{
			ExportDataDir:      args.ExportDataDir,
			FileName:           BinFileNameGroups,
			ExportPlatformName: GiteePlatformName,
			EnterpriseName:     args.EnterpriseName,
			FilePathTmpl:       ExportEnterpriseFileTmpl,
			Data:               &groupList,
			WriteJson:          true,
		}

		_, err = WriteProtoData(writeDataArgs)
		if err != nil {
			return err
		}

		recordArgs.Msg = "共落盘" + strconv.Itoa(len(groupList.GroupList)) + "条Group信息数据"
	} else {
		recordArgs.Msg = LogMsgNoData
	}
	recordTask(recordArgs)

	return nil
}

// 获取所有GroupMember
func globalGiteeDealGroupMember(args TaskArgs) error {
	recordArgs := RecordArgs{
		Log:      args.Log,
		TaskName: args.TaskName,
		Err:      nil,
		Export:   true,
	}
	// 获取Group元信息
	tmplArgs := TmplArgs{
		PageSize:        args.PageSize,
		EnterpriseId:    args.RepoInfo.EnterpriseId,
		EnterpriseToken: args.EnterpriseToken,
		StringTmpl:      GiteeV8OpenApiGetAllGroups,
	}
	baseUrl, err := FillParamIntoStringTmpl(tmplArgs)
	if err != nil {
		return err
	}

	var groupList target.AtomGitGroupList
	page := 1
	for {
		realUrl := baseUrl + strconv.Itoa(page)
		page += 1
		var data gitee.V8GroupList
		requestArgs := giteeRequestArgs{
			RequestArgs: RequestArgs{
				ExportPlatformName: GiteePlatformName,
				RealUrl:            realUrl,
				RetVal:             &data,
			},
			TaskName: args.TaskName,
			Log:      args.Log,
		}
		if err = giteeDoGetRequest(requestArgs); err != nil {
			return err
		}

		if len(data.Data) == 0 {
			break
		}

		for _, val := range data.Data {
			var group target.AtomGitGroup

			group.Id = val.ID
			group.Path = val.Path
			group.Name = val.Name
			group.Description = val.Description
			group.Public = val.Public
			group.Creator = &target.AtomGitUser{
				Id:       val.Creator.ID,
				Name:     val.Creator.Name,
				UserName: val.Creator.Username,
			}

			groupList.GroupList = append(groupList.GroupList, &group)
		}

		//recordArgs.Msg = "共获取到" + strconv.Itoa(len(memberList.MemberList)) + "条数据，但还未落盘"
		//recordTask(recordArgs)

		if int64((page-1)*args.PageSize) >= data.TotalCount {
			break
		}
	}

	if len(groupList.GroupList) == 0 && len(GiteeGroupIdList) > 0 {
		for _, groupId := range GiteeGroupIdList {
			tmplArgs = TmplArgs{
				EnterpriseId:    args.RepoInfo.EnterpriseId,
				EnterpriseToken: args.EnterpriseToken,
				RepoGroupId:     groupId,
				StringTmpl:      GiteeV8OpenApiGetGroupDetail,
			}
			realUrl, err := FillParamIntoStringTmpl(tmplArgs)
			if err != nil {
				return err
			}
			var data gitee.V8GroupDetail
			requestArgs := giteeRequestArgs{
				RequestArgs: RequestArgs{
					ExportPlatformName: GiteePlatformName,
					RealUrl:            realUrl,
					RetVal:             &data,
				},
				TaskName: args.TaskName,
				Log:      args.Log,
			}
			if err = giteeDoGetRequest(requestArgs); err != nil {
				return err
			}

			var group target.AtomGitGroup

			group.Id = data.Id
			group.Path = data.Path
			group.Name = data.Name
			group.Description = data.Description
			group.Public = data.Public
			group.Creator = &target.AtomGitUser{
				Id:       data.Creator.ID,
				Name:     data.Creator.Name,
				UserName: data.Creator.Username,
			}
			groupList.GroupList = append(groupList.GroupList, &group)
		}
	}

	// 根据上述获得的Group元信息，获取Group成员信息
	var memberList target.AtomGitGroupMemberList
	for _, group := range groupList.GroupList {
		groupId := group.Id
		tmplArgs := TmplArgs{
			PageSize:        args.PageSize,
			RepoGroupId:     groupId,
			EnterpriseId:    args.RepoInfo.EnterpriseId,
			EnterpriseToken: args.EnterpriseToken,
			StringTmpl:      GiteeV8OpenApiGetGroupMembers,
		}
		baseUrl, err := FillParamIntoStringTmpl(tmplArgs)
		if err != nil {
			return err
		}
		page = 1

		for {
			realUrl := baseUrl + strconv.Itoa(page)
			page += 1
			var data gitee.V8GroupMemberList
			requestArgs := giteeRequestArgs{
				RequestArgs: RequestArgs{
					ExportPlatformName: GiteePlatformName,
					RealUrl:            realUrl,
					RetVal:             &data,
				},
				TaskName: args.TaskName,
				Log:      args.Log,
			}
			if err = giteeDoGetRequest(requestArgs); err != nil {
				return err
			}

			if len(data.Data) == 0 {
				break
			}

			for _, val := range data.Data {
				var member target.AtomGitGroupMember
				member.User = &target.AtomGitUser{
					Id:       val.ID,
					Name:     val.Name,
					UserName: val.Username,
					Email:    val.Email,
					Phone:    val.Phone,
				}
				if role, ok := GiteeRoleMap[val.EnterpriseRole.Ident]; ok {
					member.AccessLevel = role
				} else {
					member.AccessLevel = AtomGitReporter
				}
				member.GroupName = group.Path
				memberList.MemberList = append(memberList.MemberList, &member)
			}

			if int64((page-1)*args.PageSize) >= data.TotalCount {
				break
			}
		}
	}

	// Group成员信息落盘
	if len(memberList.MemberList) > 0 {
		writeDataArgs := DataArgs{
			ExportDataDir:      args.ExportDataDir,
			FileName:           BinFileNameGroupMembers,
			ExportPlatformName: GiteePlatformName,
			EnterpriseName:     args.EnterpriseName,
			FilePathTmpl:       ExportEnterpriseFileTmpl,
			Data:               &memberList,
		}

		_, err = WriteProtoData(writeDataArgs)
		if err != nil {
			return err
		}

		recordArgs.Msg = "共落盘" + strconv.Itoa(len(memberList.MemberList)) + "条Group成员信息数据"
	} else {
		recordArgs.Msg = LogMsgNoData
	}
	recordTask(recordArgs)

	return nil
}

// Label是按企业的，不分库的
func globalGiteeDealLabel(args TaskArgs) error {
	recordArgs := RecordArgs{
		Log:      args.Log,
		TaskName: args.TaskName,
		Err:      nil,
		Export:   true,
	}
	tmplArgs := TmplArgs{
		PageSize:        args.PageSize,
		EnterpriseToken: args.EnterpriseToken,
		EnterpriseId:    args.RepoInfo.EnterpriseId,
		StringTmpl:      GiteeV8OpenApiGetEnterpriseLabels,
	}
	baseUrl, err := FillParamIntoStringTmpl(tmplArgs)
	if err != nil {
		return err
	}

	var labelList target.AtomGitLabelList
	page := 1

	for _, groupPath := range GiteeGroupPathList {
		labelList.GroupPathList = append(labelList.GroupPathList, groupPath)
	}

	for {
		realUrl := baseUrl + strconv.Itoa(page)
		page += 1

		var data gitee.V8LabelList
		requestArgs := giteeRequestArgs{
			RequestArgs: RequestArgs{
				ExportPlatformName: GiteePlatformName,
				RealUrl:            realUrl,
				RetVal:             &data,
			},
			TaskName: args.TaskName,
			Log:      args.Log,
		}
		if err = giteeDoGetRequest(requestArgs); err != nil {
			return err
		}

		if len(data.Data) == 0 {
			break
		}
		for _, val := range data.Data {
			var label target.AtomGitLabel
			label.Id = val.ID
			label.EnterpriseId = args.RepoInfo.EnterpriseId
			label.Name = val.Name
			label.Color = val.Color
			label.CreatedAt = val.CreatedAt.Unix()
			label.UpdatedAt = val.UpdatedAt.Unix()
			label.MigrateState = val.MigrateStatus
			//label.CreatedAt = timestamppb.New(val.CreatedAt)
			//label.UpdatedAt = timestamppb.New(val.UpdatedAt)
			//fmt.Println(val.CreatedAt.UTC().String())
			//fmt.Println(val.UpdatedAt.UTC().String())
			//fmt.Println(mr.CreatedAt.AsTime().String())
			//fmt.Println(mr.UpdatedAt.AsTime().String())

			labelList.LabelList = append(labelList.LabelList, &label)
		}

		if int64((page-1)*args.PageSize) >= data.TotalCount {
			break
		}
	}

	if len(labelList.LabelList) > 0 {
		writeDataArgs := DataArgs{
			ExportDataDir:      args.ExportDataDir,
			FileName:           BinFileNameLabels,
			ExportPlatformName: GiteePlatformName,
			EnterpriseName:     args.EnterpriseName,
			FilePathTmpl:       ExportEnterpriseFileTmpl,
			RepoInfo:           args.RepoInfo,
			Data:               &labelList,
		}

		_, err = WriteProtoData(writeDataArgs)
		if err != nil {
			return err
		}
		recordArgs.Msg = "共落盘" + strconv.Itoa(len(labelList.LabelList)) + "条label数据消息"
	} else {
		recordArgs.Msg = LogMsgNoData
	}
	recordTask(recordArgs)

	return nil
}

// ===========使用v8的分库的Adapter调度函数===================

// giteeDealProtectBranchSetting
// 出错不要处理，抛给adapter
func giteeDealProtectBranchSetting(args TaskArgs) error {
	recordArgs := RecordArgs{
		Log:      args.Log,
		TaskName: args.TaskName,
		Err:      nil,
		Export:   true,
	}
	userDetailArgs := giteeUserDetailArgs{
		Token:           args.Token,
		EnterpriseToken: args.EnterpriseToken,
		EnterpriseId:    args.RepoInfo.EnterpriseId,
	}
	tmplArgs := TmplArgs{
		RepoGroupName: args.RepoInfo.RepoGroupName,
		RepoName:      args.RepoInfo.RepoName,
		Token:         args.Token,
		StringTmpl:    GiteeV5OpenApiGetRepoMetaInfo,
	}
	realUrl, err := FillParamIntoStringTmpl(tmplArgs)
	if err != nil {
		return err
	}

	var repoMetaInfo gitee.Repository
	requestArgs := giteeRequestArgs{
		RequestArgs: RequestArgs{
			ExportPlatformName: GiteePlatformName,
			RealUrl:            realUrl,
			RetVal:             &repoMetaInfo,
		},
		TaskName: args.TaskName,
		Log:      args.Log,
	}
	if err = giteeDoGetRequest(requestArgs); err != nil {
		return err
	}

	var defaultAssigneeList target.AtomGitUserList
	userMap := make(map[int64]*target.AtomGitUser)

	for _, val := range repoMetaInfo.Assignee {
		var assignee target.AtomGitUser
		assignee.Id = val.ID
		assignee.Name = val.Name
		assignee.UserName = val.Login
		userDetailArgs.User = &assignee
		_ = giteeV8GetUserDetail(userDetailArgs)
		defaultAssigneeList.UserList = append(defaultAssigneeList.UserList, &assignee)
		userMap[val.ID] = &assignee
	}

	tmplArgs = TmplArgs{
		PageSize:        args.PageSize,
		RepoId:          args.RepoInfo.RepoId,
		EnterpriseToken: args.EnterpriseToken,
		EnterpriseId:    args.RepoInfo.EnterpriseId,
		StringTmpl:      GiteeV8OpenApiGetProtectBranchSetting,
	}
	baseUrl, err := FillParamIntoStringTmpl(tmplArgs)
	if err != nil {
		return err
	}

	var settingList target.AtomGitProtectBranchSettingList
	settingList.RepoId = args.RepoInfo.RepoId
	settingList.RepoName = args.RepoInfo.RepoName
	settingList.RepoGroupName = args.RepoInfo.RepoGroupName

	settingList.DefaultBranch = repoMetaInfo.DefaultBranch
	page := 1

	for {
		realUrl := baseUrl + strconv.Itoa(page)
		page += 1
		var data gitee.V8ProtectBranchSettingList
		requestArgs := giteeRequestArgs{
			RequestArgs: RequestArgs{
				ExportPlatformName: GiteePlatformName,
				RealUrl:            realUrl,
				RetVal:             &data,
			},
			TaskName: args.TaskName,
			Log:      args.Log,
		}
		if err = giteeDoGetRequest(requestArgs); err != nil {
			return err
		}

		if len(data.Data) == 0 {
			break
		}

		for _, val := range data.Data {
			var setting target.AtomGitProtectBranchSetting
			if val.BranchType.Value == GiteeBranchTypeNormal {
				// 普通分支不存在保护规则
				continue
			} else if val.BranchType.Value == GiteeBranchTypeReadOnly {
				// 只读分支
				// TODO:只读分支暂时不处理，需要和树哥沟通一下需求
				continue
			}

			// 合并Merge权限
			if val.ProtectionRule.Merger == GiteeProtectionRoleAdmin {
				setting.AllowMergeRole = append(setting.AllowMergeRole, AtomGitAdmin)
			} else if val.ProtectionRule.Merger == GiteeProtectionRoleDeveloper {
				setting.AllowMergeRole = append(setting.AllowMergeRole, AtomGitAdmin)
				setting.AllowMergeRole = append(setting.AllowMergeRole, AtomGitDeveloper)
			} else if val.ProtectionRule.Merger == GiteeProtectionRoleNone {
				// none就代表什么都用不做，role是空的
			} else {
				// 这里就说明，一定时UserId的list构成了返回值，这里只需要分割后插入即可
				userIdList := strings.Split(val.ProtectionRule.Merger, ";")
				for _, id := range userIdList {
					userId, err := strconv.Atoi(id)
					if err != nil {
						// 这里的错误一定是Gitee那边的参数出现了错误，跳过处理即可
						continue
					}
					if user, ok := userMap[int64(userId)]; ok {
						setting.AllowMergeUserList = append(setting.AllowMergeUserList, user)
					} else {
						var user target.AtomGitUser
						user.Id = int64(userId)
						userDetailArgs.User = &user
						err = giteeV8GetUserDetail(userDetailArgs)
						if err != nil {
							// 这里期望不能出现错误，因为一定是企业成员，这里记录一下吧
							recordArgs.Err = err
							recordArgs.Msg = fmt.Sprintf("获取用户信息失败，用户id：%d", userId)
							recordTask(recordArgs)
							continue
						}
						userMap[int64(userId)] = &user
						setting.AllowMergeUserList = append(setting.AllowMergeUserList, &user)
					}
				}
			}

			// 推送Push权限
			if val.ProtectionRule.Pusher == GiteeProtectionRoleAdmin {
				setting.AllowPushRole = append(setting.AllowPushRole, AtomGitAdmin)
			} else if val.ProtectionRule.Pusher == GiteeProtectionRoleDeveloper {
				setting.AllowPushRole = append(setting.AllowPushRole, AtomGitAdmin)
				setting.AllowPushRole = append(setting.AllowPushRole, AtomGitDeveloper)
			} else if val.ProtectionRule.Pusher == GiteeProtectionRoleNone {
				// none就代表什么都用不做，role是空的
			} else {
				// 这里就说明，一定时UserId的list构成了返回值，这里只需要分割后插入即可
				userIdList := strings.Split(val.ProtectionRule.Pusher, ";")
				for _, id := range userIdList {
					userId, err := strconv.Atoi(id)
					if err != nil {
						// 这里的错误一定是那边的参数出现了错误，跳过处理即可
						continue
					}
					if user, ok := userMap[int64(userId)]; ok {
						setting.AllowPushUserList = append(setting.AllowPushUserList, user)
					} else {
						var user target.AtomGitUser
						user.Id = int64(userId)
						userDetailArgs.User = &user
						err = giteeV8GetUserDetail(userDetailArgs)
						if err != nil {
							// 这里期望不能出现错误，因为一定是企业成员，这里记录一下吧
							recordArgs.Err = err
							recordArgs.Msg = fmt.Sprintf("获取用户信息失败，用户id：%d", userId)
							recordTask(recordArgs)
							continue
						}
						userMap[int64(userId)] = &user
						setting.AllowPushUserList = append(setting.AllowPushUserList, &user)
					}
				}
			}

			setting.BranchPattern = val.Name
			setting.MinimumApproval = 1
			setting.IsMergeRequestsRequired = true
			setting.IsRequireDiscussionProcessed = true
			setting.IsAllowSelfApproval = true
			setting.MergeRequestMode = AtomGitMergeRequestModeGeneral

			for _, assignee := range defaultAssigneeList.UserList {
				setting.DefaultAssigneeList = append(setting.DefaultAssigneeList, assignee)
			}

			// 允许通过Mr请求
			setting.AllowMergeRequestRole = append(setting.AllowMergeRequestRole, AtomGitAdmin)
			setting.AllowMergeRequestRole = append(setting.AllowMergeRequestRole, AtomGitDeveloper)

			settingList.ProtectBranchSettingList = append(settingList.ProtectBranchSettingList, &setting)
		}

		recordArgs.Msg = "共获取到" + strconv.Itoa(((page-2)*args.PageSize)+len(data.Data)) + "条数据，但还未落盘"
		recordTask(recordArgs)

		if int64((page-1)*args.PageSize) >= data.TotalCount {
			break
		}
	}

	if len(settingList.ProtectBranchSettingList) > 0 {
		writeDataArgs := DataArgs{
			ExportDataDir:      args.ExportDataDir,
			FileName:           BinFileNameProtectBranchSetting,
			ExportPlatformName: GiteePlatformName,
			FilePathTmpl:       ExportDataFileTmpl,
			RepoInfo:           args.RepoInfo,
			Data:               &settingList,
		}

		_, err = WriteProtoData(writeDataArgs)
		if err != nil {
			return err
		}
		recordArgs.Msg = "共落盘" + strconv.Itoa(len(settingList.ProtectBranchSettingList)) + "条分支保护规则数据消息"
	} else {
		recordArgs.Msg = LogMsgNoData
	}
	recordTask(recordArgs)

	return nil
}

// GiteeDealMileStone
// 按企业获取的MileStone数据更全面
func giteeDealMileStone(args TaskArgs) error {
	recordArgs := RecordArgs{
		Log:      args.Log,
		TaskName: args.TaskName,
		Err:      nil,
		Export:   true,
	}
	tmplArgs := TmplArgs{
		RepoId:          args.RepoInfo.RepoId,
		EnterpriseToken: args.EnterpriseToken,
		EnterpriseId:    args.RepoInfo.EnterpriseId,
		PageSize:        args.PageSize,
		StringTmpl:      GiteeV8OpenApiGetMileStones,
	}
	baseUrl, err := FillParamIntoStringTmpl(tmplArgs)
	if err != nil {
		return err
	}

	var mileStoneList target.AtomGitMileStoneList
	mileStoneList.RepoId = args.RepoInfo.RepoId
	mileStoneList.RepoName = args.RepoInfo.RepoName
	mileStoneList.RepoGroupName = args.RepoInfo.RepoGroupName

	page := 1
	for {
		realUrl := baseUrl + strconv.Itoa(page)
		page += 1

		var data gitee.V8MileStoneList
		requestArgs := giteeRequestArgs{
			RequestArgs: RequestArgs{
				ExportPlatformName: GiteePlatformName,
				RealUrl:            realUrl,
				RetVal:             &data,
			},
			TaskName: args.TaskName,
			Log:      args.Log,
		}
		if err = giteeDoGetRequest(requestArgs); err != nil {
			return err
		}

		if len(data.Data) == 0 {
			break
		}

		for _, val := range data.Data {
			var mileStone target.AtomGitMileStone
			mileStone.Id = val.ID
			mileStone.Title = val.Title
			mileStone.State = val.State
			if startDate, err := time.Parse(time.DateOnly, val.StartDate); err == nil {
				mileStone.StartDate = startDate.Unix()
			}
			if dueDate, err := time.Parse(time.DateOnly, val.DueDate); err == nil {
				mileStone.DueDate = dueDate.Unix()
			}
			mileStone.CreatedAt = val.CreatedAt.Unix()
			mileStone.UpdatedAt = val.UpdatedAt.Unix()
			mileStone.Description = val.Description
			mileStone.Assignee = &target.AtomGitUser{
				Id:       val.Assignee.ID,
				UserName: val.Assignee.Username,
				Name:     val.Assignee.Name,
			}

			mileStone.Creator = &target.AtomGitUser{
				Id:       val.Author.ID,
				UserName: val.Author.Username,
				Name:     val.Author.Name,
			}

			mileStone.IssueAllCount = val.IssueAllCount
			mileStone.IssueCompleteCount = val.IssueCompleteCount
			mileStone.MrAllCount = val.PrAllCount
			mileStone.MrCompleteCount = val.PrCompleteCount

			mileStoneList.MileStoneList = append(mileStoneList.MileStoneList, &mileStone)
		}

		recordArgs.Msg = "共获取到" + strconv.Itoa(len(mileStoneList.MileStoneList)) + "条数据，但还未落盘"
		recordTask(recordArgs)

		if int64((page-1)*args.PageSize) >= data.TotalCount {
			break
		}
	}

	if len(mileStoneList.MileStoneList) > 0 {
		dataArgs := DataArgs{
			ExportDataDir:      args.ExportDataDir,
			FileName:           BinFileNameMileStones,
			ExportPlatformName: GiteePlatformName,
			FilePathTmpl:       ExportDataFileTmpl,
			RepoInfo:           args.RepoInfo,
			Data:               &mileStoneList,
		}
		recordArgs.Msg = "共落盘" + strconv.Itoa(len(mileStoneList.MileStoneList)) + "条MileStone数据消息"
		_, err = WriteProtoData(dataArgs)
		if err != nil {
			return err
		}
	} else {
		recordArgs.Msg = LogMsgNoData
	}
	recordTask(recordArgs)

	return nil
}

// giteeDealRepoMember
// 出现错误不要处理，也不需要记录，直接返回
// adapter的调度会记录并处理错误
func giteeDealRepoMember(args TaskArgs) error {
	recordArgs := RecordArgs{
		Log:      args.Log,
		TaskName: args.TaskName,
		Err:      nil,
		Export:   true,
	}
	tmplArgs := TmplArgs{
		RepoId:          args.RepoInfo.RepoId,
		EnterpriseToken: args.EnterpriseToken,
		EnterpriseId:    args.RepoInfo.EnterpriseId,
		PageSize:        args.PageSize,
		StringTmpl:      GiteeV8OpenApiGetRepoMembers,
	}
	baseUrl, err := FillParamIntoStringTmpl(tmplArgs)
	if err != nil {
		return err
	}

	// 仓库成员权限对应
	//Gitee来源字段	Gitee Code		AtomGit目标字段	AtomGit Code
	//负责人				100				管理员			40/maintainer
	//管理员				40/master		管理员			40/maintainer
	//开发者				30/developer	开发者			30/developer
	//观察者				25/viewer		浏览者			20/guest
	//报告者				15/reporter		浏览者			20/guest
	accessMap := make(map[int64]int64)
	accessMap[100] = AtomGitAdmin
	accessMap[40] = AtomGitAdmin
	accessMap[30] = AtomGitDeveloper
	accessMap[25] = AtomGitReporter
	accessMap[15] = AtomGitReporter

	var memberList target.AtomGitRepoMemberList
	memberList.RepoId = args.RepoInfo.RepoId
	memberList.RepoName = args.RepoInfo.RepoName
	memberList.RepoGroupName = args.RepoInfo.RepoGroupName
	page := 1

	for {
		realUrl := baseUrl + strconv.Itoa(page)
		page += 1
		var data gitee.V8RepoMemberList
		requestArgs := giteeRequestArgs{
			RequestArgs: RequestArgs{
				ExportPlatformName: GiteePlatformName,
				RealUrl:            realUrl,
				RetVal:             &data,
			},
			TaskName: args.TaskName,
			Log:      args.Log,
		}
		if err = giteeDoGetRequest(requestArgs); err != nil {
			return err
		}

		if len(data.Data) == 0 {
			break
		}

		for _, val := range data.Data {
			var member target.AtomGitMember
			member.User = &target.AtomGitUser{
				Id:       val.ID,
				UserName: val.Username,
				Name:     val.Name,
				Email:    val.Email,
			}
			if access, ok := accessMap[val.ProjectAccess]; ok {
				member.AccessLevel = access
			} else {
				return errors.New(ErrorMsgAccessError)
			}
			memberList.MemberList = append(memberList.MemberList, &member)
		}

		recordArgs.Msg = "共获取到" + strconv.Itoa(len(memberList.MemberList)) + "条数据，但还未落盘"
		recordTask(recordArgs)

		if int64((page-1)*args.PageSize) >= data.TotalCount {
			break
		}
	}

	if len(memberList.MemberList) > 0 {
		writeDataArgs := DataArgs{
			ExportDataDir:      args.ExportDataDir,
			FileName:           BinFileNameRepoMembers,
			ExportPlatformName: GiteePlatformName,
			FilePathTmpl:       ExportDataFileTmpl,
			RepoInfo:           args.RepoInfo,
			Data:               &memberList,
		}

		_, err = WriteProtoData(writeDataArgs)
		if err != nil {
			return err
		}

		recordArgs.Msg = "共落盘" + strconv.Itoa(len(memberList.MemberList)) + "条仓库成员数据消息"
	} else {
		recordArgs.Msg = LogMsgNoData
	}
	recordTask(recordArgs)

	return nil
}

// ===========使用v5的分库函数===================

// giteeDealCodeReviewSetting
// 出现错误不要处理，也不需要记录，直接返回
// adapter的调度会记录并处理错误
// gitee的代码评审规则也在仓库信息中
func giteeDealCodeReviewSetting(args TaskArgs) error {
	recordArgs := RecordArgs{
		Log:      args.Log,
		TaskName: args.TaskName,
		Err:      nil,
		Export:   true,
	}
	tmplArgs := TmplArgs{
		RepoGroupName: args.RepoInfo.RepoGroupName,
		RepoName:      args.RepoInfo.RepoName,
		Token:         args.Token,
		StringTmpl:    GiteeV5OpenApiGetRepoMetaInfo,
	}
	realUrl, err := FillParamIntoStringTmpl(tmplArgs)
	if err != nil {
		return err
	}

	// 获取数据的assignee和tester就是代码评审中的设置
	// 后续的修改可以根据这两个进行赋值
	var data gitee.Repository
	requestArgs := giteeRequestArgs{
		RequestArgs: RequestArgs{
			ExportPlatformName: GiteePlatformName,
			RealUrl:            realUrl,
			RetVal:             &data,
		},
		TaskName: args.TaskName,
		Log:      args.Log,
	}
	if err = giteeDoGetRequest(requestArgs); err != nil {
		return err
	}

	// 代码评审规则
	var codeReviewSetting target.AtomGitCodeReviewSetting
	var assigner target.AtomGitUser
	assigner.Id = data.Assigner.ID
	assigner.Name = data.Assigner.Name
	assigner.UserName = data.Assigner.Login
	codeReviewSetting.DefaultReviews = append(codeReviewSetting.DefaultReviews, &assigner)

	for _, val := range data.Assignee {
		var assignee target.AtomGitUser
		assignee.Id = val.ID
		assignee.Name = val.Name
		assignee.UserName = val.Login
		codeReviewSetting.DefaultReviews = append(codeReviewSetting.DefaultReviews, &assignee)
	}

	writeDataArgs := DataArgs{
		ExportDataDir:      args.ExportDataDir,
		FileName:           BinFileNameCodeReviewSetting,
		ExportPlatformName: GiteePlatformName,
		FilePathTmpl:       ExportDataFileTmpl,
		RepoInfo:           args.RepoInfo,
		Data:               &codeReviewSetting,
	}

	_, err = WriteProtoData(writeDataArgs)
	if err != nil {
		return err
	}
	// 因为Gitee的CodeReviewSetting在仓库信息中
	recordArgs.Msg = "数据落盘成功"
	recordTask(recordArgs)

	return nil
}

func copyMetaInfoFromV5Data(metaInfo *target.AtomGitRepoMetaInfo, v5data *gitee.Repository) {
	metaInfo.Namespace.Id = v5data.Namespace.ID
	metaInfo.Namespace.Name = v5data.Namespace.Name
	metaInfo.Namespace.Path = v5data.Namespace.Path
	metaInfo.Namespace.Type = v5data.Namespace.Type
	metaInfo.Id = v5data.ID
	metaInfo.Name = v5data.Name
	metaInfo.Path = v5data.Path
	metaInfo.PathWithNamespace = v5data.FullName
	metaInfo.NameWithNamespace = v5data.HumanName
	metaInfo.Description = v5data.Description
	metaInfo.Public = v5data.Public
	metaInfo.DefaultBranch = v5data.DefaultBranch
	metaInfo.CreatedAt = v5data.CreatedAt.Unix()
	metaInfo.UpdatedAt = v5data.UpdatedAt.Unix()
	metaInfo.PushedAt = v5data.PushedAt.Unix()
	metaInfo.SshUrl = v5data.SSHURL
	metaInfo.HttpUrl = v5data.HTMLURL
}

func copyMetaInfoFromV8Data(metaInfo *target.AtomGitRepoMetaInfo, v8data *gitee.V8RepositoryDetail) {
	metaInfo.Namespace.CompleteName = v8data.Namespace.CompleteName
	metaInfo.Namespace.CompletePath = v8data.Namespace.CompletePath
	//metaInfo.PathWithNamespace = v8data.PathWithNamespace
	//metaInfo.NameWithNamespace = v8data.NameWithNamespace
	metaInfo.RepoSize = v8data.RepoSize
	metaInfo.WikiCount = v8data.WikiCount
	metaInfo.MembersCount = v8data.MembersCount
	//metaInfo.SshUrl = v8data.CloneWays.SSHURL
	//metaInfo.HttpUrl = v8data.CloneWays.HTTPSURL
}

// giteeDealRepoMetaInfo
// 出现错误不要处理，也不需要记录，直接返回
// adapter的调度会记录并处理错误
// gitee的代码评审规则也在仓库信息中
func giteeDealRepoMetaInfo(args TaskArgs) error {
	recordArgs := RecordArgs{
		Log:      args.Log,
		TaskName: args.TaskName,
		Err:      nil,
		Export:   true,
	}
	userDetailArgs := giteeUserDetailArgs{
		Token:           args.Token,
		EnterpriseToken: args.EnterpriseToken,
		EnterpriseId:    args.RepoInfo.EnterpriseId,
	}
	tmplArgs := TmplArgs{
		RepoGroupName: args.RepoInfo.RepoGroupName,
		RepoName:      args.RepoInfo.RepoName,
		Token:         args.Token,
		StringTmpl:    GiteeV5OpenApiGetRepoMetaInfo,
	}
	realUrl, err := FillParamIntoStringTmpl(tmplArgs)
	if err != nil {
		return err
	}

	var v5data gitee.Repository
	requestArgs := giteeRequestArgs{
		RequestArgs: RequestArgs{
			ExportPlatformName: GiteePlatformName,
			RealUrl:            realUrl,
			RetVal:             &v5data,
		},
		TaskName: args.TaskName,
		Log:      args.Log,
	}
	if err = giteeDoGetRequest(requestArgs); err != nil {
		return err
	}

	tmplArgs = TmplArgs{
		EnterpriseId:    args.RepoInfo.EnterpriseId,
		RepoId:          args.RepoInfo.RepoId,
		EnterpriseToken: args.EnterpriseToken,
		StringTmpl:      GiteeV8OpenApiGetRepoDetail,
	}
	realUrl, err = FillParamIntoStringTmpl(tmplArgs)
	if err != nil {
		return err
	}
	var v8data gitee.V8RepositoryDetail
	requestArgs = giteeRequestArgs{
		RequestArgs: RequestArgs{
			ExportPlatformName: GiteePlatformName,
			RealUrl:            realUrl,
			RetVal:             &v8data,
		},
		TaskName: args.TaskName,
		Log:      args.Log,
	}
	if err = giteeDoGetRequest(requestArgs); err != nil && args.EnterpriseToken != "" {
		return err
	}

	var metaInfo target.AtomGitRepoMetaInfo
	var namespace target.AtomGitNamespace
	metaInfo.Namespace = &namespace

	var creator target.AtomGitUser
	creator.Id = v5data.Owner.ID
	creator.Name = v5data.Owner.Name
	creator.UserName = v5data.Owner.Login
	userDetailArgs.User = &creator
	err = giteeV8GetUserDetail(userDetailArgs)
	if err != nil {
		_ = giteeGetUserDetail(userDetailArgs)
	}
	metaInfo.Creator = &creator
	metaInfo.OrganizationId = args.RepoInfo.EnterpriseId

	copyMetaInfoFromV5Data(&metaInfo, &v5data)
	copyMetaInfoFromV8Data(&metaInfo, &v8data)

	//fmt.Println(metaInfo.String())
	writeDataArgs := DataArgs{
		ExportDataDir:      args.ExportDataDir,
		FileName:           BinFileNameRepoMetaInfo,
		ExportPlatformName: GiteePlatformName,
		FilePathTmpl:       ExportDataFileTmpl,
		RepoInfo:           args.RepoInfo,
		Data:               &metaInfo,
	}

	_, err = WriteProtoData(writeDataArgs)
	if err != nil {
		return err
	}
	recordArgs.Msg = "数据落盘成功"
	recordTask(recordArgs)

	return nil
}

// giteeDealRelease
// 出现错误不要处理，直接返回，启动协程处会接住error，adapter的调度来判断错误
// TODO：这里又一个需求，只过滤用户自己上传的数据，不传那些自动的release
func giteeDealRelease(args TaskArgs) error {
	recordArgs := RecordArgs{
		Log:      args.Log,
		TaskName: args.TaskName,
		Err:      nil,
		Export:   true,
	}
	tmplArgs := TmplArgs{
		RepoGroupName: args.RepoInfo.RepoGroupName,
		RepoName:      args.RepoInfo.RepoName,
		Token:         args.Token,
		PageSize:      args.PageSize,
		StringTmpl:    GiteeV5OpenApiGetAllReleases,
	}
	baseUrl, err := FillParamIntoStringTmpl(tmplArgs)
	if err != nil {
		return err
	}

	var releaseList target.AtomGitReleaseList
	releaseList.RepoId = args.RepoInfo.RepoId
	releaseList.RepoName = args.RepoInfo.RepoName
	releaseList.RepoGroupName = args.RepoInfo.RepoGroupName
	page := 1

	for {
		realUrl := baseUrl + strconv.Itoa(page)
		page += 1

		var data []gitee.Release
		requestArgs := giteeRequestArgs{
			RequestArgs: RequestArgs{
				ExportPlatformName: GiteePlatformName,
				RealUrl:            realUrl,
				RetVal:             &data,
			},
			TaskName: args.TaskName,
			Log:      args.Log,
		}
		if err = giteeDoGetRequest(requestArgs); err != nil {
			return err
		}

		if len(data) == 0 {
			break
		}
		for _, val := range data {
			var release target.AtomGitRelease
			release.Id = val.ID
			release.Name = val.Name
			release.Body = val.Body
			release.PreRelease = val.Prerelease
			release.TargetCommitish = val.TargetCommitish
			release.TagName = val.TagName
			release.CreatedAt = val.CreatedAt.Unix()
			release.Author = &target.AtomGitUser{
				Id:       val.Author.ID,
				Name:     val.Author.Name,
				UserName: val.Author.Login,
			}

			for _, v := range val.Assets {
				//if v.Name == "" {
				//	continue
				//}
				var asset target.AtomGitAsset
				asset.Url = v.BrowserDownloadURL
				asset.Name = v.Name
				release.AssetsList = append(release.AssetsList, &asset)
			}

			releaseList.ReleaseList = append(releaseList.ReleaseList, &release)
		}

		recordArgs.Msg = "共获取到" + strconv.Itoa(len(releaseList.ReleaseList)) + "条数据，但还未落盘"
		recordTask(recordArgs)

	}

	if len(releaseList.ReleaseList) > 0 {
		dataArgs := DataArgs{
			ExportDataDir:      args.ExportDataDir,
			FileName:           BinFileNameReleases,
			ExportPlatformName: GiteePlatformName,
			FilePathTmpl:       ExportDataFileTmpl,
			RepoInfo:           args.RepoInfo,
			Data:               &releaseList,
		}
		recordArgs.Msg = "共落盘" + strconv.Itoa(len(releaseList.ReleaseList)) + "条数据消息"
		_, err = WriteProtoData(dataArgs)
		if err != nil {
			return err
		}
	} else {
		recordArgs.Msg = LogMsgNoData
	}
	recordTask(recordArgs)

	return nil
}

// giteeDealPushRuleSetting
// 出现错误不要处理，直接返回，启动协程处会接住error，adapter的调度来判断错误
func giteeDealPushRuleSetting(args TaskArgs) error {
	recordArgs := RecordArgs{
		Log:      args.Log,
		TaskName: args.TaskName,
		Err:      nil,
		Export:   true,
	}
	tmplArgs := TmplArgs{
		RepoGroupName: args.RepoInfo.RepoGroupName,
		RepoName:      args.RepoInfo.RepoName,
		Token:         args.Token,
		StringTmpl:    GiteeV5OpenApiGetPushRuleSetting,
	}
	realUrl, err := FillParamIntoStringTmpl(tmplArgs)
	if err != nil {
		return err
	}

	var data gitee.PushRule
	requestArgs := giteeRequestArgs{
		RequestArgs: RequestArgs{
			ExportPlatformName: GiteePlatformName,
			RealUrl:            realUrl,
			RetVal:             &data,
		},
		TaskName: args.TaskName,
		Log:      args.Log,
	}
	if err = giteeDoGetRequest(requestArgs); err != nil {
		return err
	}

	var dataList target.AtomGitPushRuleSettingList
	dataList.RepoId = args.RepoInfo.RepoId
	dataList.RepoName = args.RepoInfo.RepoName
	dataList.RepoGroupName = args.RepoInfo.RepoGroupName

	var setting target.AtomGitPushRuleSetting
	setting.RefType = "Branch"
	setting.RefName = "**"
	setting.RuleInfos = []*target.AtomGitPushInfos{
		{
			CheckerName:     "CommitMessageChecker",
			ExtraMessage:    data.CommitMessageRegex,
			FileRuleRegexes: nil,
		},
		{
			CheckerName:     "CommitAuthorEmailChecker",
			ExtraMessage:    data.AuthorEmailSuffix,
			FileRuleRegexes: nil,
		},
		{
			CheckerName:     "CommitAuthorChecker",
			ExtraMessage:    "on",
			CheckerType:     "warn",
			FileRuleRegexes: nil,
		},
		{
			CheckerName:     "CommitCommitterChecker",
			ExtraMessage:    "on",
			CheckerType:     "warn",
			FileRuleRegexes: nil,
		},
		{
			CheckerName:     "ForcePushChecker",
			ExtraMessage:    "disabled",
			FileRuleRegexes: nil,
		},
	}

	dataList.PushRuleSettingList = append(dataList.PushRuleSettingList, &setting)

	if len(dataList.PushRuleSettingList) > 0 {
		writeDataArgs := DataArgs{
			ExportDataDir:      args.ExportDataDir,
			FileName:           BinFileNamePushRuleSetting,
			ExportPlatformName: GiteePlatformName,
			FilePathTmpl:       ExportDataFileTmpl,
			RepoInfo:           args.RepoInfo,
			Data:               &dataList,
		}

		_, err = WriteProtoData(writeDataArgs)
		if err != nil {
			return err
		}
		recordArgs.Msg = "共落盘" + strconv.Itoa(len(dataList.PushRuleSettingList)) + "条推送规则数据消息"
	} else {
		recordArgs.Msg = LogMsgNoData
	}
	recordTask(recordArgs)

	return nil
}

// giteeDealWebHookSetting
// 出现错误不要处理，直接返回，启动协程处会接住error，adapter的调度来判断错误
func giteeDealWebHookSetting(args TaskArgs) error {
	recordArgs := RecordArgs{
		Log:      args.Log,
		TaskName: args.TaskName,
		Err:      nil,
		Export:   true,
	}
	tmplArgs := TmplArgs{
		RepoGroupName: args.RepoInfo.RepoGroupName,
		RepoName:      args.RepoInfo.RepoName,
		PageSize:      args.PageSize,
		Token:         args.Token,
		StringTmpl:    GiteeV5OpenApiGetWebHookSetting,
	}
	baseUrl, err := FillParamIntoStringTmpl(tmplArgs)
	if err != nil {
		return err
	}

	var webHookList target.AtomGitWebHookSettingList
	page := 1

	for {
		realUrl := baseUrl + strconv.Itoa(page)
		page += 1
		var data []gitee.WebHook
		requestArgs := giteeRequestArgs{
			RequestArgs: RequestArgs{
				ExportPlatformName: GiteePlatformName,
				RealUrl:            realUrl,
				RetVal:             &data,
			},
			TaskName: args.TaskName,
			Log:      args.Log,
		}
		if err = giteeDoGetRequest(requestArgs); err != nil {
			return err
		}

		if len(data) == 0 {
			break
		}
		for _, val := range data {
			var webHook target.AtomGitWebHookSetting
			webHook.Id = val.ID
			webHook.NotifyUrl = val.URL
			webHook.ProjectId = val.ProjectID
			webHook.CreatedAt = val.CreatedAt.Unix()
			if val.Password != "" {
				webHook.IsSetSecret = true
				webHook.Password = val.Password
			} else {
				webHook.IsSetSecret = false
			}
			webHook.IsActive = true
			webHook.ReqBodyType = AtomGitRequestTypeJson
			if val.IssuesEvents {
				webHook.Events = append(webHook.Events, GiteeEventIssue)
			}
			if val.TagPushEvents {
				webHook.Events = append(webHook.Events, GiteeEventLabel)
			}
			if val.NoteEvents {
				webHook.Events = append(webHook.Events, GiteeEventIssueComment)
				webHook.Events = append(webHook.Events, GiteeEventPullRequestReviewComment)
			}
			if val.MergeRequestsEvents {
				webHook.Events = append(webHook.Events, GiteeEventPullRequest)
			}
			if val.PushEvents {
				webHook.Events = append(webHook.Events, GiteeEventPush)
			}

			webHookList.WebHookList = append(webHookList.WebHookList, &webHook)
		}
	}

	webHookList.RepoId = args.RepoInfo.RepoId
	webHookList.RepoName = args.RepoInfo.RepoName
	webHookList.RepoGroupName = args.RepoInfo.RepoGroupName

	if len(webHookList.WebHookList) > 0 {
		writeDataArgs := DataArgs{
			ExportDataDir:      args.ExportDataDir,
			FileName:           BinFileNameWebHookSetting,
			ExportPlatformName: GiteePlatformName,
			FilePathTmpl:       ExportDataFileTmpl,
			RepoInfo:           args.RepoInfo,
			Data:               &webHookList,
		}

		_, err = WriteProtoData(writeDataArgs)
		if err != nil {
			return err
		}
		recordArgs.Msg = "共落盘" + strconv.Itoa(len(webHookList.WebHookList)) + "条数据消息"
	} else {
		recordArgs.Msg = LogMsgNoData
	}
	recordTask(recordArgs)

	return nil
}

// giteeDealDeployKeySetting
// 出现错误不要处理，直接返回，启动协程处会接住error，adapter的调度来判断错误
func giteeDealDeployKeySetting(args TaskArgs) error {
	recordArgs := RecordArgs{
		Log:      args.Log,
		TaskName: args.TaskName,
		Err:      nil,
		Export:   true,
	}
	var deployKeysList target.AtomGitDeployKeysSettingList

	tmplArgs := TmplArgs{
		RepoGroupName: args.RepoInfo.RepoGroupName,
		RepoName:      args.RepoInfo.RepoName,
		PageSize:      args.PageSize,
		Token:         args.Token,
		StringTmpl:    GiteeV5OpenApiGetDeployedKeysSetting,
	}
	baseUrl, err := FillParamIntoStringTmpl(tmplArgs)
	if err != nil {
		return err
	}
	page := 1
	for {
		realUrl := baseUrl + strconv.Itoa(page)
		page += 1
		var data []gitee.DeployKey
		requestArgs := giteeRequestArgs{
			RequestArgs: RequestArgs{
				ExportPlatformName: GiteePlatformName,
				RealUrl:            realUrl,
				RetVal:             &data,
			},
			TaskName: args.TaskName,
			Log:      args.Log,
		}
		if err = giteeDoGetRequest(requestArgs); err != nil {
			return err
		}

		if len(data) == 0 {
			break
		}

		for _, val := range data {
			var deployKey target.AtomGitDeployKeysSetting
			deployKey.Id = val.ID
			deployKey.Key = val.Key
			deployKey.Title = val.Title
			deployKeysList.DeployKeysList = append(deployKeysList.DeployKeysList, &deployKey)
		}
	}

	// CanBeDeployed
	tmplArgs = TmplArgs{
		RepoGroupName: args.RepoInfo.RepoGroupName,
		RepoName:      args.RepoInfo.RepoName,
		PageSize:      args.PageSize,
		Token:         args.Token,
		StringTmpl:    GiteeV5OpenApiGetCanBeDeployedKeysSetting,
	}
	baseUrl, err = FillParamIntoStringTmpl(tmplArgs)
	if err != nil {
		return err
	}
	page = 1

	for {
		realUrl := baseUrl + strconv.Itoa(page)
		page += 1
		var data []gitee.DeployKey
		requestArgs := giteeRequestArgs{
			RequestArgs: RequestArgs{
				ExportPlatformName: GiteePlatformName,
				RealUrl:            realUrl,
				RetVal:             &data,
			},
			TaskName: args.TaskName,
			Log:      args.Log,
		}
		if err = giteeDoGetRequest(requestArgs); err != nil {
			return err
		}

		if len(data) == 0 {
			break
		}

		for _, val := range data {
			var deployKey target.AtomGitDeployKeysSetting
			deployKey.Id = val.ID
			deployKey.Key = val.Key
			deployKey.Title = val.Title
			deployKeysList.DeployKeysList = append(deployKeysList.DeployKeysList, &deployKey)
		}
	}

	deployKeysList.RepoId = args.RepoInfo.RepoId
	deployKeysList.RepoName = args.RepoInfo.RepoName
	deployKeysList.RepoGroupName = args.RepoInfo.RepoGroupName

	if len(deployKeysList.DeployKeysList) > 0 {
		writeDataArgs := DataArgs{
			ExportDataDir:      args.ExportDataDir,
			FileName:           BinFileNameDeployKeysSetting,
			ExportPlatformName: GiteePlatformName,
			RepoInfo:           args.RepoInfo,
			FilePathTmpl:       ExportDataFileTmpl,
			Data:               &deployKeysList,
		}

		_, err = WriteProtoData(writeDataArgs)
		if err != nil {
			return err
		}
		recordArgs.Msg = "共落盘" + strconv.Itoa(len(deployKeysList.DeployKeysList)) + "条数据消息"
	} else {
		recordArgs.Msg = LogMsgNoData
	}
	recordTask(recordArgs)

	return nil
}

func giteeDealCode(args TaskArgs) (err error) {
	recordArgs := RecordArgs{
		Log:      args.Log,
		TaskName: args.TaskName,
		Err:      nil,
		Export:   true,
	}
	// 创建仓库clone对应的dir
	dataArgs := DataArgs{
		ExportDataDir:      args.ExportDataDir,
		FileName:           CloneCodeFileName,
		ExportPlatformName: GiteePlatformName,
		FilePathTmpl:       ExportDataFileTmpl,
		RepoInfo:           args.RepoInfo,
		Data:               nil, // 不写数据，只创建dir
	}
	dir, err := MkdirFromStringTmpl(dataArgs)
	if err != nil {
		return err
	}
	lastIdx := strings.LastIndex(args.RepoInfo.CodeCloneHttpUrl, "/")
	filePath := dir + args.RepoInfo.CodeCloneHttpUrl[lastIdx+1:]
	var cmd *exec.Cmd
	_, err = os.Stat(filePath)
	if os.IsNotExist(err) {
		cmd = exec.Command("git", "-C", dir, "clone", "--mirror", args.RepoInfo.CodeCloneHttpUrl)
	} else if err == nil {
		cmd = exec.Command("git", "-C", filePath, "fetch", args.RepoInfo.CodeCloneHttpUrl, "+refs/*:refs/*")
	} else {
		return err
	}
	combinedOutput, err := cmd.CombinedOutput()
	if err != nil {
		recordArgs.Msg = string(combinedOutput)
		recordArgs.Err = err
		recordTask(recordArgs)
		return err
	}

	return nil
}

func giteeDealWiki(args TaskArgs) (err error) {
	recordArgs := RecordArgs{
		Log:      args.Log,
		TaskName: args.TaskName,
		Err:      nil,
		Export:   true,
	}

	tmplArgs := TmplArgs{
		EnterpriseId:    args.RepoInfo.EnterpriseId,
		RepoId:          args.RepoInfo.RepoId,
		EnterpriseToken: args.EnterpriseToken,
		StringTmpl:      GiteeV8OpenApiGetRepoDetail,
	}
	realUrl, err := FillParamIntoStringTmpl(tmplArgs)
	if err != nil {
		return err
	}
	var data gitee.V8RepositoryDetail
	requestArgs := giteeRequestArgs{
		RequestArgs: RequestArgs{
			ExportPlatformName: GiteePlatformName,
			RealUrl:            realUrl,
			RetVal:             &data,
		},
		TaskName: args.TaskName,
		Log:      args.Log,
	}
	if err = giteeDoGetRequest(requestArgs); err != nil {
		return err
	}

	if data.WikiCount == 0 {
		recordArgs.Msg = "仓库并未开启wiki，跳过wiki的clone工作"
		recordTask(recordArgs)
		return nil
	}

	// 创建对应目录
	dataArgs := DataArgs{
		ExportDataDir:      args.ExportDataDir,
		FileName:           CloneWikiFileName,
		ExportPlatformName: GiteePlatformName,
		FilePathTmpl:       ExportDataFileTmpl,
		RepoInfo:           args.RepoInfo,
		Data:               nil, // 不写数据，只创建dir
	}

	dir, err := MkdirFromStringTmpl(dataArgs)
	if err != nil {
		return err
	}
	lastDot := strings.LastIndex(args.RepoInfo.CodeCloneHttpUrl, ".")
	wikiUrl := args.RepoInfo.CodeCloneHttpUrl[:lastDot] + ".wiki.git"
	lastIdx := strings.LastIndex(wikiUrl, "/")
	filePath := dir + wikiUrl[lastIdx+1:]

	var cmd *exec.Cmd
	_, err = os.Stat(filePath)
	if os.IsNotExist(err) {
		cmd = exec.Command("git", "-C", dir, "clone", "--mirror", wikiUrl)
	} else if err == nil {
		cmd = exec.Command("git", "-C", filePath, "fetch", wikiUrl, "+refs/*:refs/*")
	} else {
		return err
	}
	combinedOutput, err := cmd.CombinedOutput()

	if err != nil {
		recordArgs.Msg = string(combinedOutput)
		recordArgs.Err = err
		recordTask(recordArgs)
		return err
	}

	return nil
}

func giteeWorkerFuncDealIssueCmt(args *WorkerFuncArgs) error {
	tmplArgs := TmplArgs{
		RepoGroupName: args.RepoInfo.RepoGroupName,
		RepoName:      args.RepoInfo.RepoName,
		DataId:        args.DataId,
		Token:         args.Token,
		PageSize:      args.PageSize,
		StringTmpl:    GiteeV5OpenApiGetAllCommentsForOneIssue,
	}

	baseUrl, err := FillParamIntoStringTmpl(tmplArgs)
	if err != nil {
		return err
	}

	finalDataList, ok := args.Data.(*target.AtomGitIssueList)
	if !ok {
		args.Log.Error("Issue数据传递错误")
		return errors.New("Issue数据传递错误")
	}
	page := 1
	for {
		realUrl := baseUrl + strconv.Itoa(page)
		page += 1
		var data []gitee.IssueComment
		requestArgs := giteeRequestArgs{
			RequestArgs: RequestArgs{
				ExportPlatformName: GiteePlatformName,
				RealUrl:            realUrl,
				RetVal:             &data,
			},
			TaskName: args.TaskName,
			Log:      args.Log,
		}
		if err = giteeDoGetRequest(requestArgs); err != nil {
			return err
		}

		if len(data) == 0 {
			break
		}
		//args.Log.Info("MergeRequestId：", args.MrList.MergeRequestList[mrIdx].Id, "获取长度：", len(data), "url", realUrl)
		for _, val := range data {
			var cmt target.AtomGitIssueComment
			oneUser := &target.AtomGitUser{
				Id:       val.User.ID,
				UserName: val.User.Login,
				Name:     val.User.Name,
			}
			cmt.Author = oneUser
			args.UserMap.Store(val.User.ID, oneUser)

			cmt.Id = val.ID
			cmt.Body = val.Body
			cmt.CreatedAt = val.CreatedAt.Unix()
			cmt.UpdatedAt = val.UpdatedAt.Unix()
			cmt.InReplyToId = val.InReplyToID
			finalDataList.IssueList[args.FinalDataIndex].CommentList = append(finalDataList.IssueList[args.FinalDataIndex].CommentList, &cmt)
			// 需要增量导出时，按照updateTime来更新Flag
			if args.IncrementFlag &&
				!*args.AfterTimeStamp &&
				val.UpdatedAt.After(args.TimeStamp) {
				// 发现在TimeStamp之后的数据，那么就需要去查看下一页
				*args.AfterTimeStamp = true
			}
		}
	}

	return nil
}

func giteeWorkerFuncDealIssue(args *WorkerFuncArgs) (bool, error) {
	realUrl := args.ParentTaskUrl + strconv.Itoa(args.Page)

	var data []gitee.Issue
	requestArgs := giteeRequestArgs{
		RequestArgs: RequestArgs{
			ExportPlatformName: GiteePlatformName,
			RealUrl:            realUrl,
			RetVal:             &data,
		},
		TaskName: args.TaskName,
		Log:      args.Log,
	}
	if err := giteeDoGetRequest(requestArgs); err != nil {
		return false, err
	}

	var issueList target.AtomGitIssueList

	afterTimeStamp := false
	var UrlDataIds []string
	for _, val := range data {
		if args.IncrementFlag &&
			val.UpdatedAt.After(args.TimeStamp) {
			afterTimeStamp = true
		}
		var issue target.AtomGitIssue
		issue.Id = val.ID
		issue.Number = val.Number
		issue.State = val.State
		issue.Body = val.Body
		issue.Title = val.Title
		issue.CreatedAt = val.CreatedAt.Unix()
		issue.UpdatedAt = val.UpdatedAt.Unix()
		issue.FinishAt = val.FinishedAt.Unix()
		issue.Deadline = val.Deadline.Unix()
		issue.Priority = val.Priority
		issue.ScheduledTime = val.ScheduledTime
		issue.IssueType = val.IssueType
		issue.IssueState = val.IssueState
		issue.SecurityHole = val.SecurityHole
		issue.Branch = val.Branch

		creator := &target.AtomGitUser{
			Id:       val.User.ID,
			UserName: val.User.Login,
			Name:     val.User.Name,
		}
		issue.Creator = creator
		args.UserMap.Store(val.User.ID, creator)

		issue.TypeDetail = &target.AtomGitIssueTypeDetail{
			Id:        val.IssueTypeDetail.ID,
			Title:     val.IssueTypeDetail.Title,
			Template:  val.IssueTypeDetail.Template,
			Ident:     val.IssueTypeDetail.Ident,
			Color:     val.IssueTypeDetail.Color,
			IsSystem:  val.IssueTypeDetail.IsSystem,
			CreatedAt: val.IssueTypeDetail.CreatedAt.Unix(),
			UpdatedAt: val.IssueTypeDetail.UpdatedAt.Unix(),
		}

		issue.StateDetail = &target.AtomGitIssueStateDetail{
			Id:        val.IssueStateDetail.ID,
			Title:     val.IssueStateDetail.Title,
			Color:     val.IssueStateDetail.Color,
			Icon:      val.IssueStateDetail.Icon,
			Command:   val.IssueStateDetail.Command,
			Serial:    val.IssueStateDetail.Serial,
			CreatedAt: val.IssueStateDetail.CreatedAt.Unix(),
			UpdatedAt: val.IssueStateDetail.UpdatedAt.Unix(),
		}

		var labelList []*target.AtomGitLabel
		for _, label := range val.Labels {
			var oneLabel target.AtomGitLabel
			oneLabel.Id = label.ID
			oneLabel.Name = label.Name
			oneLabel.Color = label.Color
			oneLabel.EnterpriseId = args.RepoInfo.EnterpriseId
			oneLabel.CreatedAt = label.CreatedAt.Unix()
			oneLabel.UpdatedAt = label.UpdatedAt.Unix()
			labelList = append(labelList, &oneLabel)
		}
		issue.LabelList = labelList

		issue.MileStone = &target.AtomGitMileStone{
			Id:          val.Milestone.ID,
			Title:       val.Milestone.Title,
			State:       val.Milestone.State,
			CreatedAt:   val.Milestone.CreatedAt.Unix(),
			UpdatedAt:   val.Milestone.UpdatedAt.Unix(),
			Description: val.Milestone.Description,
		}

		if dueDate, err := time.Parse(time.DateOnly, val.Milestone.DueOn); err == nil {
			issue.MileStone.DueDate = dueDate.Unix()
		}

		UrlDataIds = append(UrlDataIds, val.Number)
		oneUser := &target.AtomGitUser{
			Id:       val.User.ID,
			UserName: val.User.Login,
			Name:     val.User.Name,
		}
		args.UserMap.Store(val.User.ID, oneUser)

		assignee := &target.AtomGitUser{
			Id:       val.Assignee.ID,
			UserName: val.Assignee.Login,
			Name:     val.Assignee.Name,
		}
		args.UserMap.Store(val.Assignee.ID, assignee)

		for _, collaborator := range val.Collaborators {
			oneUser = &target.AtomGitUser{
				Id:       collaborator.ID,
				UserName: collaborator.Login,
				Name:     collaborator.Name,
			}
			args.UserMap.Store(collaborator.ID, oneUser)
		}

		issueList.IssueList = append(issueList.IssueList, &issue)
	}

	// 第一次会放过，后续就会阻塞住等待上一页的任务完成
	if args.SubFuncWg != nil {
		args.SubFuncWg.Wait()
		if args.IncrementFlag &&
			!*args.AfterTimeStamp {
			return false, nil
		}
	}

	if len(data) == 0 {
		return false, nil
	}

	*args.AfterTimeStamp = afterTimeStamp
	args.SubFuncWg = &sync.WaitGroup{}
	*args.StartIndex = *args.EndIndex
	*args.EndIndex = *args.StartIndex + len(data)
	args.UrlDataIds = UrlDataIds

	v, ok := args.Data.(*target.AtomGitIssueList)
	if !ok {
		args.Log.Error("Issue类型错误")
		return false, errors.New("Issue类型错误")
	}
	v.IssueList = append(v.IssueList, issueList.IssueList...)

	recordArgs := RecordArgs{
		Log:      args.Log,
		TaskName: TaskNameIssue,
		Err:      nil,
		Export:   true,
	}
	recordArgs.Msg = "共获取到" + strconv.Itoa(len(v.IssueList)) + "条Issue数据，但还未落盘"
	recordTask(recordArgs)

	return true, nil
}

// giteeDealIssue
// 出现错误不要处理，直接返回，启动协程处会接住error，adapter的调度来判断错误
func giteeDealIssue(args TaskArgs) error {
	recordArgs := RecordArgs{
		Log:      args.Log,
		TaskName: args.TaskName,
		Err:      nil,
		Export:   true,
	}
	tmplArgs := TmplArgs{
		RepoGroupName: args.RepoInfo.RepoGroupName,
		RepoName:      args.RepoInfo.RepoName,
		Token:         args.Token,
		PageSize:      args.PageSize,
	}
	if args.IncrementFlag {
		tmplArgs.StringTmpl = GiteeV5OpenApiGetIncrementIssues
	} else {
		tmplArgs.StringTmpl = GiteeV5OpenApiGetAllIssues
	}

	baseUrl, err := FillParamIntoStringTmpl(tmplArgs)
	if err != nil {
		return err
	}

	var issueList target.AtomGitIssueList

	issueList.RepoId = args.RepoInfo.RepoId
	issueList.RepoName = args.RepoInfo.RepoName
	issueList.RepoGroupName = args.RepoInfo.RepoGroupName

	issueUsers := &sync.Map{}
	stopChan := make(chan error)
	afterTimeStamp := false

	startIndex := 0
	endIndex := 0
	poolStop := false

	workerFuncArgs := WorkerFuncArgs{
		ParentTaskUrl:  baseUrl,
		Page:           1,
		UserMap:        issueUsers,
		Log:            args.Log,
		TaskName:       args.TaskName,
		SubFuncWg:      nil,
		StopChan:       stopChan,
		PoolStop:       &poolStop,
		RepoInfo:       args.RepoInfo,
		Token:          args.Token,
		PageSize:       args.PageSize,
		IncrementFlag:  args.IncrementFlag,
		AfterTimeStamp: &afterTimeStamp,
		TimeStamp:      args.TimeStamp,
		Data:           &issueList,
		StartIndex:     &startIndex,
		EndIndex:       &endIndex,
		FinalDataIndex: 0,
		DataId:         "",
	}

	task := &WorkerTask{
		Parent:     true,
		Args:       workerFuncArgs,
		ParentFunc: giteeWorkerFuncDealIssue,
		ChildFunc:  giteeWorkerFuncDealIssueCmt,
	}

	GlobalWorkerPool.Submit(task)

	err = <-stopChan
	if err != nil {
		return err
	}

	close(stopChan)
	var userList target.AtomGitUserList
	var rangeError error
	rangeError = nil
	issueUsers.Range(func(key, val any) bool {
		v, ok := val.(*target.AtomGitUser)
		if !ok {
			rangeError = errors.New("userMap中数据出现错误")
			return false
		}
		userList.UserList = append(userList.UserList, v)
		return true
	})

	if rangeError != nil {
		return rangeError
	}

	if len(userList.UserList) > 0 {
		userListArgs := DataArgs{
			ExportDataDir:      args.ExportDataDir,
			ExportPlatformName: GiteePlatformName,
			FilePathTmpl:       ExportDataFileTmpl,
			RepoInfo:           args.RepoInfo,
			Data:               &userList,
		}
		if args.IncrementFlag {
			userListArgs.FileName = BinFileNameIssueUsersIncrement
			recordArgs.Msg = "增量导出，共落盘" + strconv.Itoa(len(userList.UserList)) + "条用户数据的消息"
		} else {
			userListArgs.FileName = BinFileNameIssueUsers
			recordArgs.Msg = "共落盘" + strconv.Itoa(len(userList.UserList)) + "条用户数据的消息"
		}
		_, err = WriteProtoData(userListArgs)
		if err != nil {
			return err
		}
	} else {
		recordArgs.Msg = "Issue评论的用户" + LogMsgNoData
	}
	recordTask(recordArgs)

	if len(issueList.IssueList) > 0 {
		issueListArgs := DataArgs{
			ExportDataDir:      args.ExportDataDir,
			ExportPlatformName: GiteePlatformName,
			FilePathTmpl:       ExportDataFileTmpl,
			RepoInfo:           args.RepoInfo,
			Data:               &issueList,
		}
		if args.IncrementFlag {
			issueListArgs.FileName = BinFileNameIssuesIncrement
			recordArgs.Msg = "增量导出，共落盘" + strconv.Itoa(len(issueList.IssueList)) + "条Issue数据消息"
		} else {
			issueListArgs.FileName = BinFileNameIssues
			recordArgs.Msg = "共落盘" + strconv.Itoa(len(issueList.IssueList)) + "条Issue数据消息"
		}
		_, err = WriteProtoData(issueListArgs)
		if err != nil {
			return err
		}
	} else {
		recordArgs.Msg = "Issue" + LogMsgNoData
	}
	recordTask(recordArgs)

	return nil
}

func giteeWorkerFuncDealMrCmt(args *WorkerFuncArgs) error {
	tmplArgs := TmplArgs{
		RepoGroupName: args.RepoInfo.RepoGroupName,
		RepoName:      args.RepoInfo.RepoName,
		DataId:        args.DataId,
		Token:         args.Token,
		PageSize:      args.PageSize,
		StringTmpl:    GiteeV5OpenApiGetAllCommentsForOneMergeRequest,
	}

	baseUrl, err := FillParamIntoStringTmpl(tmplArgs)
	if err != nil {
		return err
	}

	finalDataList, ok := args.Data.(*target.AtomGitMergeRequestList)
	if !ok {
		args.Log.Error("Mr数据传递错误")
		return errors.New("mr数据传递错误")
	}
	page := 1
	for {
		realUrl := baseUrl + strconv.Itoa(page)
		page += 1
		var data []gitee.MergeRequestComment
		requestArgs := giteeRequestArgs{
			RequestArgs: RequestArgs{
				ExportPlatformName: GiteePlatformName,
				RealUrl:            realUrl,
				RetVal:             &data,
			},
			TaskName: args.TaskName,
			Log:      args.Log,
		}
		if err = giteeDoGetRequest(requestArgs); err != nil {
			return err
		}

		if len(data) == 0 {
			break
		}
		//args.Log.Info("MergeRequestId：", args.MrList.MergeRequestList[mrIdx].Id, "获取长度：", len(data), "url", realUrl)
		for _, val := range data {
			var cmt target.AtomGitMergeRequestComment
			oneUser := &target.AtomGitUser{
				Id:       val.User.ID,
				UserName: val.User.Login,
				Name:     val.User.Name,
			}
			cmt.Author = oneUser
			args.UserMap.Store(val.User.ID, oneUser)
			cmt.Id = val.ID
			cmt.Body = val.Body
			cmt.FilePath = val.Path
			cmt.LineNum = val.NewLine
			cmt.CreatedAt = val.CreatedAt.Unix()
			cmt.UpdatedAt = val.UpdatedAt.Unix()
			cmt.SourceProjectId = args.RepoInfo.RepoId
			cmt.InReplyToId = val.InReplyToID
			cmt.MergeRequestId = finalDataList.MergeRequestList[args.FinalDataIndex].Id
			finalDataList.MergeRequestList[args.FinalDataIndex].CommentList = append(finalDataList.MergeRequestList[args.FinalDataIndex].CommentList, &cmt)

			// 需要增量导出时，按照updateTime来更新Flag
			if args.IncrementFlag &&
				!*args.AfterTimeStamp &&
				val.UpdatedAt.After(args.TimeStamp) {
				// 发现在TimeStamp之后的数据，那么就需要去查看下一页
				*args.AfterTimeStamp = true
			}
		}
	}

	//args.Log.Info("第", args.FinalDataIndex, "个Mr的Comment的个数为:", len(finalDataList.MergeRequestList[args.FinalDataIndex].CommentList))

	return nil
}

func giteeWorkerFuncDealMr(args *WorkerFuncArgs) (bool, error) {
	realUrl := args.ParentTaskUrl + strconv.Itoa(args.Page)

	var data []gitee.MergeRequest
	requestArgs := giteeRequestArgs{
		RequestArgs: RequestArgs{
			ExportPlatformName: GiteePlatformName,
			RealUrl:            realUrl,
			RetVal:             &data,
		},
		TaskName: args.TaskName,
		Log:      args.Log,
	}
	if err := giteeDoGetRequest(requestArgs); err != nil {
		return false, err
	}

	var mrList target.AtomGitMergeRequestList
	afterTimeStamp := false
	var UrlDataIds []string
	for _, val := range data {
		if args.IncrementFlag &&
			val.UpdatedAt.After(args.TimeStamp) {
			afterTimeStamp = true
		}
		var mr target.AtomGitMergeRequest
		mr.Id = val.ID
		mr.Title = val.Title
		mr.Body = val.Body
		mr.State = val.State
		mr.Locked = val.Locked
		mr.CreatedAt = val.CreatedAt.Unix()
		mr.UpdatedAt = val.UpdatedAt.Unix()
		mr.ClosedAt = val.ClosedAt.Unix()
		mr.MergedAt = val.MergedAt.Unix()
		mr.MergeAble = val.Mergeable
		mr.CanMergeCheck = val.CanMergeCheck
		oneUser := &target.AtomGitUser{
			Id:       val.User.ID,
			UserName: val.User.Login,
			Name:     val.User.Name,
		}
		args.UserMap.Store(val.User.ID, oneUser)
		mr.Creator = oneUser

		mr.SourceRepoId = val.Head.Repo.ID
		mr.SourceRepoPath = val.Head.Repo.Path
		mr.SourceRepoGroupPath = val.Head.Repo.Namespace.Path
		mr.SourceRepoRefName = val.Head.Ref
		mr.SourceRepoRefSha = val.Head.Sha

		mr.TargetRepoId = val.Base.Repo.ID
		mr.TargetRepoPath = val.Base.Repo.Path
		mr.TargetRepoGroupPath = val.Base.Repo.Namespace.Path
		mr.TargetRepoRefName = val.Base.Ref
		mr.TargetRepoRefSha = val.Base.Sha
		for _, label := range val.Labels {
			oneLabel := &target.AtomGitLabel{
				Id:           label.Id,
				Name:         label.Name,
				Color:        label.Color,
				EnterpriseId: args.RepoInfo.EnterpriseId,
				CreatedAt:    label.CreatedAt.Unix(),
				UpdatedAt:    label.UpdatedAt.Unix(),
			}
			mr.LabelList = append(mr.LabelList, oneLabel)
		}

		for _, assigner := range val.Assignees {
			oneUser := &target.AtomGitUser{
				Id:       assigner.ID,
				UserName: assigner.Login,
				Name:     assigner.Name,
			}
			args.UserMap.Store(assigner.ID, oneUser)

			mrAssignee := &target.AtomGitAssignee{
				User:   oneUser,
				Accept: assigner.Accept,
			}
			mr.AssigneeList = append(mr.AssigneeList, mrAssignee)
		}

		for _, tester := range val.Testers {
			oneUser := &target.AtomGitUser{
				Id:       tester.ID,
				UserName: tester.Login,
				Name:     tester.Name,
			}
			args.UserMap.Store(tester.ID, oneUser)

			mrTester := &target.AtomGitTester{
				User:   oneUser,
				Accept: tester.Accept,
			}
			mr.TesterList = append(mr.TesterList, mrTester)
		}

		UrlDataIds = append(UrlDataIds, strconv.FormatInt(val.Number, 10))
		mrList.MergeRequestList = append(mrList.MergeRequestList, &mr)
	}

	// 第一次会放过，后续就会阻塞住等待上一页任务完成
	if args.SubFuncWg != nil {
		args.SubFuncWg.Wait()

		// 增量导出Flag并且在没有TimeStamp之后的数据时，停止获取
		if args.IncrementFlag &&
			!*args.AfterTimeStamp {
			return false, nil
		}
	}

	if len(data) == 0 {
		return false, nil
	}

	*args.AfterTimeStamp = afterTimeStamp
	args.SubFuncWg = &sync.WaitGroup{}
	*args.StartIndex = *args.EndIndex
	*args.EndIndex = *args.StartIndex + len(data)
	args.UrlDataIds = UrlDataIds

	v, ok := args.Data.(*target.AtomGitMergeRequestList)
	if !ok {
		args.Log.Error("mr类型错误")
		return false, errors.New("mr类型错误")
	}
	v.MergeRequestList = append(v.MergeRequestList, mrList.MergeRequestList...)

	recordArgs := RecordArgs{
		Log:      args.Log,
		TaskName: TaskNameMergeRequest,
		Err:      nil,
		Export:   true,
	}
	recordArgs.Msg = "共获取到" + strconv.Itoa(len(v.MergeRequestList)) + "条MR数据，但还未落盘"
	recordTask(recordArgs)

	return true, nil
}

// giteeDealMergeRequest
// 出现错误不要处理，直接返回，启动协程处会接住error，adapter的调度来判断错误
func giteeDealMergeRequest(args TaskArgs) error {
	recordArgs := RecordArgs{
		Log:      args.Log,
		TaskName: args.TaskName,
		Err:      nil,
		Export:   true,
	}
	tmplArgs := TmplArgs{
		RepoGroupName: args.RepoInfo.RepoGroupName,
		RepoName:      args.RepoInfo.RepoName,
		Token:         args.Token,
		PageSize:      args.PageSize,
	}
	if args.IncrementFlag {
		tmplArgs.StringTmpl = GiteeV5OpenApiGetIncrementMergeRequests
	} else {
		tmplArgs.StringTmpl = GiteeV5OpenApiGetAllMergeRequests
	}

	baseUrl, err := FillParamIntoStringTmpl(tmplArgs)
	if err != nil {
		return err
	}

	var mrList target.AtomGitMergeRequestList
	mrList.RepoId = args.RepoInfo.RepoId
	mrList.RepoName = args.RepoInfo.RepoName
	mrList.RepoGroupName = args.RepoInfo.RepoGroupName
	mrUsers := &sync.Map{}

	stopChan := make(chan error)
	afterTimeStamp := false

	startIndex := 0
	endIndex := 0
	poolStop := false

	workerFuncArgs := WorkerFuncArgs{
		ParentTaskUrl:  baseUrl,
		Page:           1,
		UserMap:        mrUsers,
		Log:            args.Log,
		TaskName:       args.TaskName,
		SubFuncWg:      nil,
		StopChan:       stopChan,
		PoolStop:       &poolStop,
		RepoInfo:       args.RepoInfo,
		Token:          args.Token,
		PageSize:       args.PageSize,
		IncrementFlag:  args.IncrementFlag,
		AfterTimeStamp: &afterTimeStamp,
		TimeStamp:      args.TimeStamp,
		Data:           &mrList,
		StartIndex:     &startIndex,
		EndIndex:       &endIndex,
		FinalDataIndex: 0,
		DataId:         "",
	}

	task := &WorkerTask{
		Parent:     true,
		Args:       workerFuncArgs,
		ParentFunc: giteeWorkerFuncDealMr,
		ChildFunc:  giteeWorkerFuncDealMrCmt,
	}

	//recordArgs.Msg = "任务推送至WorkerPool"
	//recordTask(recordArgs)
	GlobalWorkerPool.Submit(task)

	err = <-stopChan
	if err != nil {
		return err
	}

	close(stopChan)

	var userList target.AtomGitUserList
	var rangeError error
	rangeError = nil
	mrUsers.Range(func(key, val any) bool {
		v, ok := val.(*target.AtomGitUser)
		if !ok {
			rangeError = errors.New("userMap中数据出现错误")
			return false
		}
		userList.UserList = append(userList.UserList, v)
		return true
	})

	if rangeError != nil {
		return rangeError
	}

	if len(userList.UserList) > 0 {
		userListArgs := DataArgs{
			ExportDataDir:      args.ExportDataDir,
			ExportPlatformName: GiteePlatformName,
			FilePathTmpl:       ExportDataFileTmpl,
			RepoInfo:           args.RepoInfo,
			Data:               &userList,
		}
		if args.IncrementFlag {
			userListArgs.FileName = BinFileNameMergeRequestUsersIncrement
			recordArgs.Msg = "增量导出，共落盘" + strconv.Itoa(len(userList.UserList)) + "条用户数据的消息"
		} else {
			userListArgs.FileName = BinFileNameMergeRequestUsers
			recordArgs.Msg = "共落盘" + strconv.Itoa(len(userList.UserList)) + "条用户数据的消息"
		}
		_, err = WriteProtoData(userListArgs)
		if err != nil {
			return err
		}
	} else {
		recordArgs.Msg = "MergeRequest评论的用户" + LogMsgNoData
	}
	recordTask(recordArgs)

	if len(mrList.MergeRequestList) > 0 {
		mrListArgs := DataArgs{
			ExportDataDir:      args.ExportDataDir,
			ExportPlatformName: GiteePlatformName,
			FilePathTmpl:       ExportDataFileTmpl,
			RepoInfo:           args.RepoInfo,
			Data:               &mrList,
		}
		if args.IncrementFlag {
			mrListArgs.FileName = BinFileNameMergeRequestsIncrement
			recordArgs.Msg = "增量数据，共落盘" + strconv.Itoa(len(mrList.MergeRequestList)) + "条Mr数据消息"
		} else {
			mrListArgs.FileName = BinFileNameMergeRequests
			recordArgs.Msg = "共落盘" + strconv.Itoa(len(mrList.MergeRequestList)) + "条Mr数据消息"
		}
		_, err = WriteProtoData(mrListArgs)
		if err != nil {
			return err
		}
	} else {
		recordArgs.Msg = "MergeRequest" + LogMsgNoData
	}
	recordTask(recordArgs)

	return nil
}

// ParallelReadUsers
// 并发加速根据仓库来读取用户
func giteeMergeUsersByRepo(args MergeArgs) error {
	insert := false

	dataArgs := DataArgs{
		ExportDataDir:      args.BaseConfig.ExportDataDir,
		ExportPlatformName: args.BaseConfig.ExportPlatformName,
		FilePathTmpl:       ExportDataFileTmpl,
		RepoInfo:           args.Repo,
	}

	// 加载增量导出mr中的User
	var incrementMrUserList target.AtomGitUserList
	dataArgs.FileName = BinFileNameMergeRequestUsersIncrement
	dataArgs.Data = &incrementMrUserList
	_, err := ReadProtoData(dataArgs)
	if err == nil || os.IsNotExist(err) {
		// 文件不存在的错误可以忽略，因为可以理解成没有增量导出，直接merge即可
		// 文件不存在时，返回的len(incrementUserList) == 0，所以逻辑合并
		for _, val := range incrementMrUserList.UserList {
			// 新建对象是为了incrementUserList的垃圾回收
			user := target.AtomGitUser{
				Id:       val.Id,
				UserName: val.UserName,
				Name:     val.Name,
				Email:    val.Email,
				Phone:    val.Phone,
			}
			args.UserMap.Store(val.Id, &user)
			insert = true
		}
	} else {
		return err
	}

	// 加载增量导出Issue中的User
	var incrementIssueUserList target.AtomGitUserList
	dataArgs.FileName = BinFileNameIssueUsersIncrement
	dataArgs.Data = &incrementIssueUserList
	_, err = ReadProtoData(dataArgs)
	if err == nil || os.IsNotExist(err) {
		// 文件不存在的错误可以忽略，因为可以理解成没有增量导出，直接merge即可
		// 文件不存在时，返回的len(incrementIssueUserList) == 0，所以逻辑合并
		for _, val := range incrementIssueUserList.UserList {
			// 新建对象是为了incrementUserList的垃圾回收
			user := target.AtomGitUser{
				Id:       val.Id,
				UserName: val.UserName,
				Name:     val.Name,
				Email:    val.Email,
				Phone:    val.Phone,
			}
			args.UserMap.Store(val.Id, &user)
			insert = true
		}
	} else {
		return err
	}

	// 加载全量导出mr中的user
	var mrUserList target.AtomGitUserList
	dataArgs.FileName = BinFileNameMergeRequestUsers
	dataArgs.Data = &mrUserList
	_, err = ReadProtoData(dataArgs)
	if err == nil || os.IsNotExist(err) {
		for _, val := range mrUserList.UserList {
			user := target.AtomGitUser{
				Id:       val.Id,
				UserName: val.UserName,
				Name:     val.Name,
				Email:    val.Email,
				Phone:    val.Phone,
			}
			args.UserMap.Store(val.Id, &user)
			insert = true
		}
	} else {
		return err
	}

	// 加载comment和mr中的user
	var issueUserList target.AtomGitUserList
	dataArgs.FileName = BinFileNameIssueUsers
	dataArgs.Data = &issueUserList
	_, err = ReadProtoData(dataArgs)
	if err == nil || os.IsNotExist(err) {
		for _, val := range issueUserList.UserList {
			user := target.AtomGitUser{
				Id:       val.Id,
				UserName: val.UserName,
				Name:     val.Name,
				Email:    val.Email,
				Phone:    val.Phone,
			}
			args.UserMap.Store(val.Id, &user)
			insert = true
		}
	} else {
		return err
	}

	// 加载库member中的user
	var memberList target.AtomGitRepoMemberList
	dataArgs.FileName = BinFileNameRepoMembers
	dataArgs.Data = &memberList
	_, err = ReadProtoData(dataArgs)
	if err == nil || os.IsNotExist(err) {
		for _, val := range memberList.MemberList {
			user := target.AtomGitUser{
				Id:       val.User.Id,
				UserName: val.User.UserName,
				Name:     val.User.Name,
				Email:    val.User.Email,
				Phone:    val.User.Phone,
			}
			args.UserMap.Store(val.User.Id, &user)
			insert = true
		}
	} else {
		return err
	}

	_ = insert
	// 不记录合并日志
	//if insert && args.Log != nil {
	//	msg := fmt.Sprintf("仓库-【%s/%s】仓库相关用户参与了去重", args.Repo.RepoGroupName, args.Repo.RepoName)
	//	args.Log.Info(msg)
	//}

	return nil
}

// MergeAllUsers
// 通用函数，并发合并某一平台所有的用户
func giteeMergeAllUsers(cfg *common.Config) error {
	// 用户去重使用
	userMap := &sync.Map{}
	platformName := GetPlatformName(cfg.BaseConfig.ExportPlatformName)
	// 找到所有已导出仓库，因此调用为导入准备的函数
	repos, err := GetAllRepoInfo(cfg)
	if err != nil {
		return err
	}

	// 加载之前已经完成的数据
	var ExportedUsers target.AtomGitUserList
	dataArgs := DataArgs{
		ExportDataDir:      cfg.BaseConfig.ExportDataDir,
		ExportPlatformName: platformName,
		FileName:           BinFileNameAllUsersProto,
		EnterpriseName:     cfg.BaseConfig.EnterpriseName,
		FilePathTmpl:       ExportEnterpriseFileTmpl,
		RepoInfo:           nil,
		Data:               &ExportedUsers,
	}
	_, err = ReadProtoData(dataArgs)
	if err == nil {
		// 文件存在，正常加载
		for _, val := range ExportedUsers.UserList {
			userMap.Store(val.Id, val)
		}
	} else {
		// 文件不存在或是出错，不需要停止，不用处理任何逻辑
	}

	args := MergeArgs{
		UserMap:    userMap,
		BaseConfig: &cfg.BaseConfig,
		Log:        cfg.GlobalLog,
	}

	waitGroup := sync.WaitGroup{}
	var readError error
	readError = nil
	// 分库的每个库的用户
	for idx := range repos {
		waitGroup.Add(1)
		args.Repo = &repos[idx]
		go func(funcArgs MergeArgs) {
			err := giteeMergeUsersByRepo(funcArgs)
			if err != nil {
				readError = err
			}
			waitGroup.Done()
		}(args)
	}

	waitGroup.Wait()
	if readError != nil {
		return readError
	}

	// 组成员，优先级第二
	var groupMembers target.AtomGitGroupMemberList
	readDataArgs := DataArgs{
		ExportDataDir:      cfg.BaseConfig.ExportDataDir,
		ExportPlatformName: cfg.BaseConfig.ExportPlatformName,
		FileName:           BinFileNameGroupMembers,
		EnterpriseName:     cfg.BaseConfig.EnterpriseName,
		FilePathTmpl:       ExportEnterpriseFileTmpl,
		Data:               &groupMembers,
	}
	_, err = ReadProtoData(readDataArgs)
	if err == nil || os.IsNotExist(err) {
		for _, val := range groupMembers.MemberList {
			userMap.Store(val.User.Id, val.User)
		}
	} else if err != nil {
		return err
	}

	// 企业成员，数据最全所有优先级最高
	// 企业成员一定具有全部信息，所以优先级最高
	var enterpriseMembers target.AtomGitEnterpriseMemberList
	readDataArgs = DataArgs{
		ExportDataDir:      cfg.BaseConfig.ExportDataDir,
		ExportPlatformName: cfg.BaseConfig.ExportPlatformName,
		FileName:           BinFileNameEnterpriseMembersProto,
		EnterpriseName:     cfg.BaseConfig.EnterpriseName,
		FilePathTmpl:       ExportEnterpriseFileTmpl,
		Data:               &enterpriseMembers,
	}
	_, err = ReadProtoData(readDataArgs)
	if err == nil || os.IsNotExist(err) {
		for _, val := range enterpriseMembers.MemberList {
			userMap.Store(val.User.Id, val.User)
		}
	} else if err != nil {
		return err
	}

	// 将所有去重的用户，加入list
	var AllUsers target.AtomGitUserList
	var rangeError error
	rangeError = nil
	userMap.Range(func(key, val any) bool {
		v, ok := val.(*target.AtomGitUser)
		if !ok {
			rangeError = errors.New("userMap中数据出现错误")
			return false
		}
		AllUsers.UserList = append(AllUsers.UserList, v)
		return true
	})
	if rangeError != nil {
		return rangeError
	}

	// 尝试获取全部用户的邮箱信息
	waitGroup = sync.WaitGroup{}
	var requestError error
	requestError = nil
	for idx := range AllUsers.UserList {
		waitGroup.Add(1)
		user := AllUsers.UserList[idx]
		go func(user *target.AtomGitUser) {
			userDetailArgs := giteeUserDetailArgs{
				Token:           cfg.BaseConfig.Token,
				EnterpriseToken: cfg.BaseConfig.EnterpriseToken,
				User:            user,
			}
			errV8 := giteeV8GetUserDetail(userDetailArgs)
			if errV8 != nil {
				//cfg.GlobalLog.Error(errV8)
				errV5 := giteeGetUserDetail(userDetailArgs)
				if errV5 != nil {
					cfg.GlobalLog.Error(errV5)
				}
			}
			waitGroup.Done()
		}(user)
	}

	waitGroup.Wait()

	dataArgs = DataArgs{
		ExportDataDir:      cfg.BaseConfig.ExportDataDir,
		ExportPlatformName: platformName,
		FileName:           BinFileNameAllUsersProto,
		EnterpriseName:     cfg.BaseConfig.EnterpriseName,
		FilePathTmpl:       ExportEnterpriseFileTmpl,
		Data:               &AllUsers,
	}

	_, err = WriteProtoData(dataArgs)
	if err != nil {
		return err
	}

	dataArgs = DataArgs{
		ExportDataDir:      cfg.BaseConfig.ExportDataDir,
		ExportPlatformName: platformName,
		FileName:           BinFileNameAllUsersJson,
		EnterpriseName:     cfg.BaseConfig.EnterpriseName,
		FilePathTmpl:       ExportEnterpriseFileTmpl,
		Data:               &AllUsers,
		WriteJson:          true,
	}

	_, err = WriteProtoData(dataArgs)
	if err != nil {
		return err
	}

	cfg.GlobalLog.Info("User-Merge共落盘", strconv.Itoa(len(AllUsers.UserList)), "条用户信息")

	if requestError != nil {
		return requestError
	}
	return nil
}

// ParallelReadMergeRequests
// 并发加速根据仓库来读取MergeRequest
func giteeMergeMergeRequestsByRepo(args MergeArgs) error {
	// 最终的MrList
	var finalData target.AtomGitMergeRequestList
	// Set去重用
	mrIdSet := make(map[int64]struct{})
	dataArgs := DataArgs{
		ExportDataDir:      args.BaseConfig.ExportDataDir,
		ExportPlatformName: args.BaseConfig.ExportPlatformName,
		FilePathTmpl:       ExportDataFileTmpl,
		RepoInfo:           args.Repo,
	}

	// 增量数据遍历
	var incrementMr target.AtomGitMergeRequestList
	dataArgs.FileName = BinFileNameMergeRequestsIncrement
	dataArgs.Data = &incrementMr
	incrementMrFilePath, err := ReadProtoData(dataArgs)
	if err == nil || os.IsNotExist(err) {
		// 只要是增量数据，就确定是最新的，直接加入finalData
		for _, val := range incrementMr.MergeRequestList {
			finalData.MergeRequestList = append(finalData.MergeRequestList, val)
			mrIdSet[val.Id] = struct{}{}
		}
	} else if err != nil {
		// 出现其他错误
		return err
	}

	// 全量数据遍历
	var mrList target.AtomGitMergeRequestList
	dataArgs.FileName = BinFileNameMergeRequests
	dataArgs.Data = &mrList
	_, err = ReadProtoData(dataArgs)
	if err == nil || os.IsNotExist(err) {
		// 全量文件不存在时，依然执行Merge，只不过无数据
		for _, val := range mrList.MergeRequestList {
			// 增量数据中的已有的，全量数据不再添加
			if _, ok := mrIdSet[val.Id]; !ok {
				finalData.MergeRequestList = append(finalData.MergeRequestList, val)
				mrIdSet[val.Id] = struct{}{}
			}
		}
	} else if err != nil {
		// 出现其他错误
		return err
	}

	// 将finalData覆盖写入全量数据文件中
	if len(finalData.MergeRequestList) > 0 {
		// 将finalData覆盖写入全量数据文件中
		dataArgs.FileName = BinFileNameMergeRequestsMerge
		dataArgs.Data = &finalData
		_, err = WriteProtoData(dataArgs)
		if err != nil {
			return err
		}

		_ = incrementMrFilePath
		// merge后不删除增量文件
		//_, err = os.Stat(incrementMrFilePath)
		//if err == nil {
		//	err = os.Remove(incrementMrFilePath)
		//	if err != nil {
		//		return err
		//	}
		//}

		// 不记录合并日志
		//if args.Log != nil {
		//	msg := fmt.Sprintf("仓库-【%s/%s】仓库的mr完成了增量和全量的合并", args.Repo.RepoGroupName, args.Repo.RepoName)
		//	args.Log.Info(msg)
		//}
	}

	return nil
}

// MergeMergeRequestsByRepo
// 通用函数，按照库来合并Mr的增量导出数据和全量导出数据
// 完成合并后，按照库写入Mr的全量导出文件，按照平台不同调用具体的业务函数
func giteeMergeAllMergeRequests(cfg *common.Config) error {
	GetPlatformName(cfg.BaseConfig.ExportPlatformName)

	// 加载所有的已导出仓库，因此调用为导入准备的函数
	repos, err := GetAllRepoInfo(cfg)
	if err != nil {
		return err
	}

	args := MergeArgs{
		BaseConfig: &cfg.BaseConfig,
		UserMap:    nil, // 不需要UserMap
		Log:        cfg.GlobalLog,
	}

	waitGroup := sync.WaitGroup{}
	var readError error
	readError = nil
	for idx := range repos {
		waitGroup.Add(1)
		args.Repo = &repos[idx]
		go func(funcArgs MergeArgs) {
			err = giteeMergeMergeRequestsByRepo(funcArgs)
			if err != nil {
				readError = err
			}
			waitGroup.Done()
		}(args)
	}

	waitGroup.Wait()
	if readError != nil {
		return readError
	}

	return nil
}

// giteeMergeIssueByRepo
// 并发加速根据仓库来读取Issue
func giteeMergeIssuesByRepo(args MergeArgs) error {
	var finalData target.AtomGitIssueList
	// Set去重用
	issueIdSet := make(map[int64]struct{})
	dataArgs := DataArgs{
		ExportDataDir:      args.BaseConfig.ExportDataDir,
		ExportPlatformName: args.BaseConfig.ExportPlatformName,
		FilePathTmpl:       ExportDataFileTmpl,
		RepoInfo:           args.Repo,
	}

	// 增量数据遍历
	var incrementIssue target.AtomGitIssueList
	dataArgs.FileName = BinFileNameIssuesIncrement
	dataArgs.Data = &incrementIssue
	incrementIssueFilePath, err := ReadProtoData(dataArgs)
	if err == nil || os.IsNotExist(err) {
		// 只要是增量数据，就确定是最新的，直接加入finalData
		for _, val := range incrementIssue.IssueList {
			finalData.IssueList = append(finalData.IssueList, val)
			issueIdSet[val.Id] = struct{}{}
		}
	} else if err != nil {
		// 出现其他错误
		return err
	}

	// 全量数据遍历
	var issueList target.AtomGitIssueList
	dataArgs.FileName = BinFileNameIssues
	dataArgs.Data = &issueList
	_, err = ReadProtoData(dataArgs)
	if err == nil || os.IsNotExist(err) {
		// 全量文件不存在时，依然执行Merge，只不过无数据
		for _, val := range issueList.IssueList {
			// 增量数据中的已有的，全量数据不再添加
			if _, ok := issueIdSet[val.Id]; !ok {
				finalData.IssueList = append(finalData.IssueList, val)
				issueIdSet[val.Id] = struct{}{}
			}
		}
	} else if err != nil {
		// 出现其他错误
		return err
	}

	// 将finalData覆盖写入全量数据文件中
	if len(finalData.IssueList) > 0 {
		// 将finalData覆盖写入全量数据文件中
		dataArgs.FileName = BinFileNameIssuesMerge
		dataArgs.Data = &finalData
		_, err = WriteProtoData(dataArgs)
		if err != nil {
			return err
		}

		_ = incrementIssueFilePath
		// merge后不删除增量文件
		//_, err = os.Stat(incrementIssueFilePath)
		//if err == nil {
		//	err = os.Remove(incrementIssueFilePath)
		//	if err != nil {
		//		return err
		//	}
		//}

		// 不记录合并日志
		//if args.Log != nil {
		//	msg := fmt.Sprintf("仓库-【%s/%s】仓库的Issue完成了增量和全量的合并", args.Repo.RepoGroupName, args.Repo.RepoName)
		//	args.Log.Info(msg)
		//}
	}

	return nil
}

// MergeIssuesByRepo
// 通用函数，按照库来合并Issue的增量导出数据和全量导出数据
// 完成合并后，按照库写入Issue的全量导出文件，按照平台不同调用具体的业务函数
func giteeMergeAllIssues(cfg *common.Config) error {
	GetPlatformName(cfg.BaseConfig.ExportPlatformName)
	// 加载所有的已导出仓库，因此调用为导入准备的函数
	repos, err := GetAllRepoInfo(cfg)
	if err != nil {
		return err
	}

	args := MergeArgs{
		BaseConfig: &cfg.BaseConfig,
		UserMap:    nil, // 不使用UserMap
		Log:        cfg.GlobalLog,
	}

	// 按库遍历
	waitGroup := sync.WaitGroup{}
	var readError error
	readError = nil
	for idx := range repos {
		args.Repo = &repos[idx]
		waitGroup.Add(1)
		go func(funcArgs MergeArgs) {
			err = giteeMergeIssuesByRepo(funcArgs)
			if err != nil {
				readError = err
			}
			waitGroup.Done()
		}(args)
	}

	waitGroup.Wait()
	if readError != nil {
		return readError
	}

	return nil
}
